1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /* XXX KEBE ASKS --> Is this affected instead by the IPFILTER.LICENCE? */
13
14 /*
15 * Copyright 2019, Joyent, Inc.
16 */
17
18 #if defined(KERNEL) || defined(_KERNEL)
19 # undef KERNEL
20 # undef _KERNEL
21 # define KERNEL 1
22 # define _KERNEL 1
23 #endif
24 #include <sys/errno.h>
25 #include <sys/types.h>
26 #include <sys/param.h>
27 #include <sys/time.h>
28 #include <sys/socket.h>
29 #include <net/if.h>
30 #include <net/route.h>
31 #include <netinet/in.h>
32 #include <netinet/in_systm.h>
33 #include <netinet/ip.h>
34 #include <netinet/ip_var.h>
35 #include <netinet/tcp.h>
36 #include "netinet/ip_compat.h"
37 #ifdef USE_INET6
38 #include <netinet/icmp6.h>
39 #endif
40 #include <netinet/tcpip.h>
41 #include "netinet/ip_fil.h"
42 #include "netinet/ip_nat.h"
43 #include "netinet/ip_frag.h"
44 #include "netinet/ip_state.h"
45 #include "netinet/ip_proxy.h"
46 #include "netinet/ip_auth.h"
47 #include "netinet/ipf_stack.h"
48 #ifdef IPFILTER_SCAN
49 # include "netinet/ip_scan.h"
50 #endif
51 #ifdef IPFILTER_SYNC
52 # include "netinet/ip_sync.h"
53 #endif
54 #include "netinet/ip_pool.h"
55 #include "netinet/ip_htable.h"
56 #ifdef IPFILTER_COMPILED
57 # include "netinet/ip_rules.h"
58 #endif
59 #if defined(_KERNEL)
60 #include <sys/sunddi.h>
61 #endif
62
63 /* Extra includes outside normal ipf things. */
64 #include <sys/types.h>
65 #include <inet/ip6.h>
66 /* Because ipf compiles this kernel file in userland testing... */
67 #ifndef ASSERT3U
68 #define ASSERT3U(a, b, c) ASSERT(a ## b ## c);
69 #endif /* ASSERT3U */
70
71 /*
72 * cfw == Cloud Firewall ==> routines for a global-zone data collector about
73 * ipf events for SmartOS.
74 *
75 * The variable below is mdb-hackable to experiment with turning it on and
76 * off. Eventually this will tie into a new ipf (GZ-only) device that flips
77 * this on when there is an open instance.
78 */
79 boolean_t ipf_cfwlog_enabled;
80
81 /*
82 * XXX KEBE ASKS MOVE THIS TO A HEADER FILE?
83 */
84 #define CFWEV_BLOCK 1
85 #define CFWEV_BEGIN 2
86 #define CFWEV_END 3
87 #define CFWDIR_IN 1
88 #define CFWDIR_OUT 2
89 typedef struct cfwev_s {
90 uint16_t cfwev_type; /* BEGIN, END, BLOCK */
91 uint8_t cfwev_protocol; /* IPPROTO_* */
92 uint8_t cfwev_direction;
93 /*
94 * The above "direction" informs if src/dst are local/remote or
95 * remote/local.
96 */
97 uint16_t cfwev_sport; /* Source port */
98 uint16_t cfwev_dport; /* Dest. port */
99 in6_addr_t cfwev_saddr; /* Can be clever later with unions, w/not. */
100 in6_addr_t cfwev_daddr;
101 /* XXX KEBE ASKS hrtime for relative time from some start instead? */
102 struct timeval cfwev_tstamp;
103 zoneid_t cfwev_zonedid; /* Pullable from ipf_stack_t. */
104 uint32_t cfwev_ruleid; /* Pullable from fr_info_t. */
105 } cfwev_t;
106
107 #ifdef _KERNEL
108 static inline zoneid_t
109 ifs_to_did(ipf_stack_t *ifs)
110 {
111 if (ifs->ifs_zone_did == 0) {
112 zone_t *zone;
113
114 /*
115 * Because we can't get the zone_did at initialization time
116 * because most zone data isn't readily available then,
117 * cement the did in place now.
118 */
119 ASSERT(ifs->ifs_zone != GLOBAL_ZONEID);
120 zone = zone_find_by_id(ifs->ifs_zone);
121 if (zone != NULL) {
122 ifs->ifs_zone_did = zone->zone_did;
123 zone_rele(zone);
124 }
125 /* Else we are either in shutdown or something weirder. */
126 }
127 return (ifs->ifs_zone_did);
128 }
129
130 /*
131 * ipf_block_cfwlog()
132 *
133 * Called by fr_check(). Record drop events for a global-zone data collector.
134 * Use rest-of-ipf-style names for the parameters.
135 */
136 void
137 ipf_block_cfwlog(frentry_t *fr, fr_info_t *fin, ipf_stack_t *ifs)
138 {
139 cfwev_t event = {0};
140
141 /*
142 * We need a rule.
143 * Capture failure by using dtrace on this function's entry.
144 * 'ipf_block_cfwlog:entry /arg0 == NULL/ { printf("GOTCHA!\n"); }'
145 */
146 if (fr == NULL)
147 return;
148
149 event.cfwev_type = CFWEV_BLOCK;
150 /*
151 * IPF code elsewhere does the cheesy single-flag check, even thogh
152 * there are two flags in a rule (one for in, one for out).
153 */
154 event.cfwev_direction = (fr->fr_flags & FR_INQUE) ?
155 CFWDIR_IN : CFWDIR_OUT;
156
157 event.cfwev_protocol = fin->fin_p;
158 /* XXX KEBE SAYS ICMP stuff should fall in here too. */
159 event.cfwev_sport = fin->fin_sport;
160 event.cfwev_dport = fin->fin_dport;
161
162 if (fin->fin_v == IPV4_VERSION) {
163 IN6_INADDR_TO_V4MAPPED(&fin->fin_src, &event.cfwev_saddr);
164 IN6_INADDR_TO_V4MAPPED(&fin->fin_dst, &event.cfwev_daddr);
165 } else {
166 ASSERT3U(fin->fin_v, ==, IPV6_VERSION);
167 event.cfwev_saddr = fin->fin_src6.in6;
168 event.cfwev_daddr = fin->fin_dst6.in6;
169 }
170
171 /*
172 * XXX KEBE ASKS -> something better instead?!?
173 * uniqtime() is what ipf's GETKTIME() uses. It does give us tv_usec,
174 * but I'm not sure if it's suitable for what we need.
175 */
176 uniqtime(&event.cfwev_tstamp);
177 event.cfwev_zonedid = ifs_to_did(ifs);
178 event.cfwev_ruleid = fin->fin_rule;
179
180 DTRACE_PROBE1(ipf__cfw__block, cfwev_t *, &event);
181 }
182
183 /*
184 * ipf_log_cfwlog()
185 *
186 * Twin of ipstate_log(), but records state events for a global-zone data
187 * collector.
188 */
189 void
190 ipf_log_cfwlog(struct ipstate *is, uint_t type, ipf_stack_t *ifs)
191 {
192 cfwev_t event = {0};
193
194 switch (type) {
195 case ISL_NEW:
196 case ISL_CLONE:
197 event.cfwev_type = CFWEV_BEGIN;
198 break;
199 case ISL_EXPIRE:
200 case ISL_FLUSH:
201 case ISL_REMOVE:
202 case ISL_KILLED:
203 case ISL_ORPHAN:
204 #if 0
205 event.cfwev_type = CFWEV_END;
206 break;
207 #else
208 /* We don't care about disappearances in CFW logging for now. */
209 return;
210 #endif
211 default:
212 event.cfwev_type = CFWEV_BLOCK;
213 break;
214 }
215
216 /*
217 * IPF code elsewhere does the cheesy single-flag check, even thogh
218 * there are two flags in a rule (one for in, one for out).
219 */
220 event.cfwev_direction = (is->is_rule->fr_flags & FR_INQUE) ?
221 CFWDIR_IN : CFWDIR_OUT;
222 event.cfwev_protocol = is->is_p;
223 switch (is->is_p) {
224 case IPPROTO_TCP:
225 case IPPROTO_UDP:
226 event.cfwev_sport = is->is_sport;
227 event.cfwev_dport = is->is_dport;
228 break;
229 case IPPROTO_ICMP:
230 case IPPROTO_ICMPV6:
231 /* Scribble the ICMP type in sport... */
232 event.cfwev_sport = is->is_icmp.ici_type;
233 break;
234 }
235
236 if (is->is_v == IPV4_VERSION) {
237 IN6_INADDR_TO_V4MAPPED(&is->is_src.in4, &event.cfwev_saddr);
238 IN6_INADDR_TO_V4MAPPED(&is->is_dst.in4, &event.cfwev_daddr);
239 } else {
240 ASSERT3U(is->is_v, ==, IPV6_VERSION);
241 event.cfwev_saddr = is->is_src.in6;
242 event.cfwev_daddr = is->is_dst.in6;
243 }
244
245 /*
246 * XXX KEBE ASKS -> something better instead?!?
247 * uniqtime() is what ipf's GETKTIME() uses. It does give us tv_usec,
248 * but I'm not sure if it's suitable for what we need.
249 */
250 uniqtime(&event.cfwev_tstamp);
251 event.cfwev_zonedid = ifs_to_did(ifs);
252 /* XXX KEBE ASKS -> good enough? */
253 event.cfwev_ruleid = is->is_rulen;
254
255 /* XXX KEBE SAYS Then we do something with it. */
256 DTRACE_PROBE1(ipf__cfw__state, cfwev_t *, &event);
257 }
258
259 #else
260 /* Blank stubs to satisfy userland's test stuff. */
261
262 void
263 ipf_log_cfwlog(struct ipstate *a, uint_t b, ipf_stack_t *c)
264 {
265 }
266
267 void
268 ipf_block_cfwlog(frentry_t *a, fr_info_t *b, ipf_stack_t *c)
269 {
270 }
271
272 #endif /* _KERNEL */