--- old/usr/src/cmd/ipf/tools/Makefile.tools	Thu Mar 14 17:20:51 2019
+++ new/usr/src/cmd/ipf/tools/Makefile.tools	Thu Mar 14 17:20:51 2019
@@ -35,7 +35,7 @@
 IPMON_OBJS=	ipmon.o ipfzone.o ipmon_y.o ipmon_l.o
 IPNAT_OBJS=	ipnat.o ipfzone.o ipnat_y.o ipnat_l.o
 IPPOOL_OBJS=	ippool.o ipfzone.o ippool_y.o ippool_l.o
-IPFTEST_OBJS=	ipftest.o ipfzone.o \
+IPFTEST_OBJS=	cfw.o ipftest.o ipfzone.o \
 		ip_fil.o ip_state.o ip_compat.o \
 		ip_frag.o ip_nat.o ip_nat6.o fil.o \
 		ip_htable.o ip_lookup.o \
--- old/usr/src/uts/common/Makefile.files	Thu Mar 14 17:20:52 2019
+++ new/usr/src/uts/common/Makefile.files	Thu Mar 14 17:20:52 2019
@@ -1790,9 +1790,9 @@
 
 SCSA2USB_OBJS += scsa2usb.o usb_ms_bulkonly.o usb_ms_cbi.o
 
-IPF_OBJS += ip_fil_solaris.o fil.o solaris.o ip_state.o ip_frag.o ip_nat.o \
-	    ip_proxy.o ip_auth.o ip_pool.o ip_htable.o ip_lookup.o \
-	    ip_log.o misc.o ip_compat.o ip_nat6.o drand48.o
+IPF_OBJS += cfw.o ip_fil_solaris.o fil.o solaris.o ip_state.o ip_frag.o \
+	    ip_nat.o ip_proxy.o ip_auth.o ip_pool.o ip_htable.o ip_lookup.o \
+	    ip_log.o misc.o ip_compat.o ip_nat6.o drand48.o 
 
 IPD_OBJS += ipd.o
 
--- /dev/null	Thu Mar 14 17:20:52 2019
+++ new/usr/src/uts/common/inet/ipf/cfw.c	Thu Mar 14 17:20:52 2019
@@ -0,0 +1,272 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/* XXX KEBE ASKS --> Is this affected instead by the IPFILTER.LICENCE? */
+
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
+
+#if defined(KERNEL) || defined(_KERNEL)
+# undef KERNEL
+# undef _KERNEL
+# define        KERNEL	1
+# define        _KERNEL	1
+#endif
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include "netinet/ip_compat.h"
+#ifdef	USE_INET6
+#include <netinet/icmp6.h>
+#endif
+#include <netinet/tcpip.h>
+#include "netinet/ip_fil.h"
+#include "netinet/ip_nat.h"
+#include "netinet/ip_frag.h"
+#include "netinet/ip_state.h"
+#include "netinet/ip_proxy.h"
+#include "netinet/ip_auth.h"
+#include "netinet/ipf_stack.h"
+#ifdef IPFILTER_SCAN
+# include "netinet/ip_scan.h"
+#endif
+#ifdef IPFILTER_SYNC
+# include "netinet/ip_sync.h"
+#endif
+#include "netinet/ip_pool.h"
+#include "netinet/ip_htable.h"
+#ifdef IPFILTER_COMPILED
+# include "netinet/ip_rules.h"
+#endif
+#if defined(_KERNEL)
+#include <sys/sunddi.h>
+#endif
+
+/* Extra includes outside normal ipf things. */
+#include <sys/types.h>
+#include <inet/ip6.h>
+/* Because ipf compiles this kernel file in userland testing... */
+#ifndef ASSERT3U
+#define	ASSERT3U(a, b, c) ASSERT(a ## b ## c);
+#endif	/* ASSERT3U */
+
+/*
+ * cfw == Cloud Firewall ==> routines for a global-zone data collector about
+ * ipf events for SmartOS.
+ *
+ * The variable below is mdb-hackable to experiment with turning it on and
+ * off. Eventually this will tie into a new ipf (GZ-only) device that flips
+ * this on when there is an open instance.
+ */
+boolean_t ipf_cfwlog_enabled;
+
+/*
+ * XXX KEBE ASKS MOVE THIS TO A HEADER FILE?
+ */
+#define	CFWEV_BLOCK	1
+#define	CFWEV_BEGIN	2
+#define	CFWEV_END	3
+#define	CFWDIR_IN	1
+#define	CFWDIR_OUT	2
+typedef struct cfwev_s {
+	uint16_t cfwev_type;	/* BEGIN, END, BLOCK */
+	uint8_t cfwev_protocol;	/* IPPROTO_* */
+	uint8_t cfwev_direction;
+	/*
+	 * The above "direction" informs if src/dst are local/remote or
+	 * remote/local.
+	 */
+	uint16_t cfwev_sport;	/* Source port */
+	uint16_t cfwev_dport;	/* Dest. port */
+	in6_addr_t cfwev_saddr;	/* Can be clever later with unions, w/not. */
+	in6_addr_t cfwev_daddr;
+	/* XXX KEBE ASKS hrtime for relative time from some start instead? */
+	struct timeval cfwev_tstamp;
+	zoneid_t cfwev_zonedid;	/* Pullable from ipf_stack_t. */
+	uint32_t cfwev_ruleid;	/* Pullable from fr_info_t. */
+} cfwev_t;
+
+#ifdef _KERNEL
+static inline zoneid_t
+ifs_to_did(ipf_stack_t *ifs)
+{
+	if (ifs->ifs_zone_did == 0) {
+		zone_t *zone;
+
+		/*
+		 * Because we can't get the zone_did at initialization time
+		 * because most zone data isn't readily available then,
+		 * cement the did in place now.
+		 */
+		ASSERT(ifs->ifs_zone != GLOBAL_ZONEID);
+		zone = zone_find_by_id(ifs->ifs_zone);
+		if (zone != NULL) {
+			ifs->ifs_zone_did = zone->zone_did;
+			zone_rele(zone);
+		}
+		/* Else we are either in shutdown or something weirder. */
+	}
+	return (ifs->ifs_zone_did);
+}
+
+/*
+ * ipf_block_cfwlog()
+ *
+ * Called by fr_check().  Record drop events for a global-zone data collector.
+ * Use rest-of-ipf-style names for the parameters.
+ */
+void
+ipf_block_cfwlog(frentry_t *fr, fr_info_t *fin, ipf_stack_t *ifs)
+{
+	cfwev_t event = {0};
+
+	/*
+	 * We need a rule.
+	 * Capture failure by using dtrace on this function's entry.
+	 * 'ipf_block_cfwlog:entry /arg0 == NULL/ { printf("GOTCHA!\n"); }'
+	 */
+	if (fr == NULL)
+		return;
+
+	event.cfwev_type = CFWEV_BLOCK;
+	/*
+	 * IPF code elsewhere does the cheesy single-flag check, even thogh
+	 * there are two flags in a rule (one for in, one for out).
+	 */
+	event.cfwev_direction = (fr->fr_flags & FR_INQUE) ?
+	    CFWDIR_IN : CFWDIR_OUT;
+
+	event.cfwev_protocol = fin->fin_p;
+	/* XXX KEBE SAYS ICMP stuff should fall in here too. */
+	event.cfwev_sport = fin->fin_sport;
+	event.cfwev_dport = fin->fin_dport;
+
+	if (fin->fin_v == IPV4_VERSION) {
+		IN6_INADDR_TO_V4MAPPED(&fin->fin_src, &event.cfwev_saddr);
+		IN6_INADDR_TO_V4MAPPED(&fin->fin_dst, &event.cfwev_daddr);
+	} else {
+		ASSERT3U(fin->fin_v, ==, IPV6_VERSION);
+		event.cfwev_saddr = fin->fin_src6.in6;
+		event.cfwev_daddr = fin->fin_dst6.in6;
+	}
+
+	/*
+	 * XXX KEBE ASKS -> something better instead?!?
+	 * uniqtime() is what ipf's GETKTIME() uses. It does give us tv_usec,
+	 * but I'm not sure if it's suitable for what we need.
+	 */
+	uniqtime(&event.cfwev_tstamp);
+	event.cfwev_zonedid = ifs_to_did(ifs);
+	event.cfwev_ruleid = fin->fin_rule;
+
+	DTRACE_PROBE1(ipf__cfw__block, cfwev_t *, &event);
+}
+
+/*
+ * ipf_log_cfwlog()
+ *
+ * Twin of ipstate_log(), but records state events for a global-zone data
+ * collector.
+ */
+void
+ipf_log_cfwlog(struct ipstate *is, uint_t type, ipf_stack_t *ifs)
+{
+	cfwev_t event = {0};
+
+	switch (type) {
+	case ISL_NEW:
+	case ISL_CLONE:
+		event.cfwev_type = CFWEV_BEGIN;
+		break;
+	case ISL_EXPIRE:
+	case ISL_FLUSH:
+	case ISL_REMOVE:
+	case ISL_KILLED:
+	case ISL_ORPHAN:
+#if 0
+		event.cfwev_type = CFWEV_END;
+		break;
+#else
+		/* We don't care about disappearances in CFW logging for now. */
+		return;
+#endif
+	default:
+		event.cfwev_type = CFWEV_BLOCK;
+		break;
+	}
+
+	/*
+	 * IPF code elsewhere does the cheesy single-flag check, even thogh
+	 * there are two flags in a rule (one for in, one for out).
+	 */
+	event.cfwev_direction = (is->is_rule->fr_flags & FR_INQUE) ?
+	    CFWDIR_IN : CFWDIR_OUT;
+	event.cfwev_protocol = is->is_p;
+	switch (is->is_p) {
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+		event.cfwev_sport = is->is_sport;
+		event.cfwev_dport = is->is_dport;
+		break;
+	case IPPROTO_ICMP:
+	case IPPROTO_ICMPV6:
+		/* Scribble the ICMP type in sport... */
+		event.cfwev_sport = is->is_icmp.ici_type;
+		break;
+	}
+
+	if (is->is_v == IPV4_VERSION) {
+		IN6_INADDR_TO_V4MAPPED(&is->is_src.in4, &event.cfwev_saddr);
+		IN6_INADDR_TO_V4MAPPED(&is->is_dst.in4, &event.cfwev_daddr);
+	} else {
+		ASSERT3U(is->is_v, ==, IPV6_VERSION);
+		event.cfwev_saddr = is->is_src.in6;
+		event.cfwev_daddr = is->is_dst.in6;
+	}
+
+	/*
+	 * XXX KEBE ASKS -> something better instead?!?
+	 * uniqtime() is what ipf's GETKTIME() uses. It does give us tv_usec,
+	 * but I'm not sure if it's suitable for what we need.
+	 */
+	uniqtime(&event.cfwev_tstamp);
+	event.cfwev_zonedid = ifs_to_did(ifs);
+	/* XXX KEBE ASKS -> good enough? */
+	event.cfwev_ruleid = is->is_rulen;
+
+	/* XXX KEBE SAYS Then we do something with it. */
+	DTRACE_PROBE1(ipf__cfw__state, cfwev_t *, &event);
+}
+
+#else
+/* Blank stubs to satisfy userland's test stuff. */
+
+void
+ipf_log_cfwlog(struct ipstate *a, uint_t b, ipf_stack_t *c)
+{
+}
+
+void
+ipf_block_cfwlog(frentry_t *a, fr_info_t *b, ipf_stack_t *c)
+{
+}
+
+#endif	/* _KERNEL */
--- old/usr/src/uts/common/inet/ipf/fil.c	Thu Mar 14 17:20:52 2019
+++ new/usr/src/uts/common/inet/ipf/fil.c	Thu Mar 14 17:20:52 2019
@@ -2588,6 +2588,9 @@
 	}
 #endif
 
+	if (IFS_CFWLOG(ifs) && FR_ISBLOCK(pass))
+		ipf_block_cfwlog(fr, fin, ifs);
+
 	/*
 	 * The FI_STATE flag is cleared here so that calling fr_checkstate
 	 * will work when called from inside of fr_fastroute.  Although
--- old/usr/src/uts/common/inet/ipf/ip_state.c	Thu Mar 14 17:20:52 2019
+++ new/usr/src/uts/common/inet/ipf/ip_state.c	Thu Mar 14 17:20:52 2019
@@ -1524,6 +1524,9 @@
 	if (ifs->ifs_ipstate_logging)
 		ipstate_log(is, ISL_NEW, ifs);
 
+	if (IFS_CFWLOG(ifs))
+		ipf_log_cfwlog(is, ISL_NEW, ifs);
+
 	RWLOCK_EXIT(&ifs->ifs_ipf_state);
 	fin->fin_rev = IP6_NEQ(&is->is_dst, &fin->fin_daddr);
 	fin->fin_flx |= FI_STATE;
@@ -2314,6 +2317,8 @@
 		is->is_flags &= ~(SI_W_SPORT|SI_W_DPORT);
 		if ((flags & SI_CLONED) && ifs->ifs_ipstate_logging)
 			ipstate_log(is, ISL_CLONE, ifs);
+		if ((flags & SI_CLONED) && IFS_CFWLOG(ifs))
+			ipf_log_cfwlog(is, ISL_CLONE, ifs);
 	}
 
 	ret = -1;
@@ -3397,7 +3402,15 @@
  
 	if (ifs->ifs_ipstate_logging != 0 && why != 0)
 		ipstate_log(is, why, ifs);
-
+#if 0
+	/*
+	 * For now, ipf_log_cfwlog() copes with all "why" values.
+	 * strictly speaking, though, they all map to one event, which for
+	 * now is not supported.
+	 */
+	if (why != 0 && IFS_CFWLOG(ifs))
+		ipf_log_cfwlog(is, why, ifs);
+#endif
 	if (is->is_rule != NULL) {
 		is->is_rule->fr_statecnt--;
 		(void)fr_derefrule(&is->is_rule, ifs);
@@ -3931,7 +3944,6 @@
 	return rval;
 }
 
-
 /* ------------------------------------------------------------------------ */
 /* Function:    ipstate_log                                                 */
 /* Returns:     Nil                                                         */
--- old/usr/src/uts/common/inet/ipf/netinet/ip_fil.h	Thu Mar 14 17:20:53 2019
+++ new/usr/src/uts/common/inet/ipf/netinet/ip_fil.h	Thu Mar 14 17:20:53 2019
@@ -8,7 +8,7 @@
  *
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  *
- * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
+ * Copyright 2019, Joyent, Inc.
  */
 
 #ifndef	__IP_FIL_H__
@@ -1559,6 +1559,14 @@
 			    ipf_stack_t *));
 extern	void	fr_logunload __P((ipf_stack_t *));
 
+/* SmartOS single-FD global-zone state accumulator (see cfw.c) */
+extern boolean_t ipf_cfwlog_enabled;
+struct ipstate;	/* Ugggh. */
+extern void ipf_log_cfwlog __P((struct ipstate *, uint_t, ipf_stack_t *));
+extern void ipf_block_cfwlog __P((frentry_t *, fr_info_t *, ipf_stack_t *));
+#define	IFS_CFWLOG(ifs) ((ifs)->ifs_gz_controlled && ipf_cfwlog_enabled)
+
+
 extern	frentry_t	*fr_acctpkt __P((fr_info_t *, u_32_t *));
 extern	int		fr_copytolog __P((int, char *, int));
 extern	u_short		fr_cksum __P((mb_t *, ip_t *, int, void *));
--- old/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h	Thu Mar 14 17:20:53 2019
+++ new/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h	Thu Mar 14 17:20:53 2019
@@ -6,7 +6,7 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright 2018 Joyent, Inc.  All rights reserved.
+ * Copyright 2019, Joyent, Inc.
  */
 
 #ifndef	__IPF_STACK_H__
@@ -46,6 +46,7 @@
 	struct ipf_stack	*ifs_gz_cont_ifs;
 	netid_t			ifs_netid;
 	zoneid_t		ifs_zone;
+	zoneid_t		ifs_zone_did;
 	boolean_t		ifs_gz_controlled;
 
 	/* ipf module */
@@ -316,6 +317,7 @@
 	char			*ifs_addmask_key;
 	char			*ifs_rn_zeros;
 	char			*ifs_rn_ones;
+
 #ifdef KERNEL
 	/* kstats for inbound and outbound */
 	kstat_t			*ifs_kstatp[2];
--- old/usr/src/uts/intel/ipf/ipf.global-objs.debug64	Thu Mar 14 17:20:53 2019
+++ new/usr/src/uts/intel/ipf/ipf.global-objs.debug64	Thu Mar 14 17:20:53 2019
@@ -68,6 +68,7 @@
 idletime_tab
 ip6exthdr
 ipf_cb_ops
+ipf_cfwlog_enabled
 ipf_dev_info
 ipf_devfiles
 ipf_eth_bcast_addr
--- old/usr/src/uts/sparc/ipf/ipf.global-objs.debug64	Thu Mar 14 17:20:53 2019
+++ new/usr/src/uts/sparc/ipf/ipf.global-objs.debug64	Thu Mar 14 17:20:53 2019
@@ -60,6 +60,7 @@
 idletime_tab
 ip6exthdr
 ipf_cb_ops
+ipf_cfwlog_enabled
 ipf_dev_info
 ipf_devfiles
 ipf_kstat_tmp
