xref: /xnu-12377.81.4/bsd/net/droptap.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 /*
2  * Copyright (c) 2024 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <sys/queue.h>
30 #include <sys/socketvar.h>
31 #include <net/bpf.h>
32 #include <net/droptap.h>
33 #include <net/if_ports_used.h>
34 #include <net/if_var_private.h>
35 #include <net/kpi_interface.h>
36 #include <net/pktap.h>
37 
38 
39 struct droptap_softc {
40 	LIST_ENTRY(droptap_softc)         dtap_link;
41 	uint32_t                          dtap_unit;
42 	uint32_t                          dtap_dlt_pktap_count;
43 	struct ifnet                      *dtap_ifp;
44 };
45 
46 static int droptap_inited = 0;
47 
48 SYSCTL_DECL(_net_link);
49 SYSCTL_NODE(_net_link, OID_AUTO, droptap,
50     CTLFLAG_RW  | CTLFLAG_LOCKED, 0, "droptap virtual interface");
51 
52 uint32_t droptap_total_tap_count = 0;
53 SYSCTL_UINT(_net_link_droptap, OID_AUTO, total_tap_count,
54     CTLFLAG_RD | CTLFLAG_LOCKED, &droptap_total_tap_count, 0, "");
55 
56 uint32_t droptap_verbose = 0;
57 SYSCTL_UINT(_net_link_droptap, OID_AUTO, verbose,
58     CTLFLAG_RW | CTLFLAG_LOCKED, &droptap_verbose, 0, "");
59 
60 static LCK_GRP_DECLARE(droptap_lck_grp, "droptap");
61 static LCK_ATTR_DECLARE(droptap_lck_attr, 0, 0);
62 static LCK_RW_DECLARE_ATTR(droptap_lck_rw, &droptap_lck_grp, &droptap_lck_attr);
63 
64 
65 static LIST_HEAD(droptap_list, droptap_softc) droptap_list =
66     LIST_HEAD_INITIALIZER(droptap_list);
67 
68 int droptap_clone_create(struct if_clone *, u_int32_t, void *);
69 int droptap_clone_destroy(struct ifnet *);
70 
71 #define DROPTAP_MAXUNIT IF_MAXUNIT
72 
73 static struct if_clone droptap_cloner =
74     IF_CLONE_INITIALIZER(DROPTAP_IFNAME,
75     droptap_clone_create,
76     droptap_clone_destroy,
77     0,
78     DROPTAP_MAXUNIT);
79 
80 errno_t droptap_if_output(ifnet_t, mbuf_t);
81 errno_t droptap_add_proto(ifnet_t, protocol_family_t,
82     const struct ifnet_demux_desc *, u_int32_t);
83 errno_t droptap_del_proto(ifnet_t, protocol_family_t);
84 errno_t droptap_tap_callback(ifnet_t, u_int32_t, bpf_tap_mode);
85 void droptap_detach(ifnet_t);
86 static void droptap_bpf_tap_packet(kern_packet_t, uint32_t,
87     struct droptap_header *, uint32_t, struct ifnet *, pid_t,
88     const char *, pid_t, const char *, uint8_t, uint32_t);
89 static void droptap_bpf_tap_mbuf(struct mbuf *, uint16_t,
90     struct droptap_header *, struct ifnet *);
91 
92 
93 void
droptap_init(void)94 droptap_init(void)
95 {
96 	int error = 0;
97 
98 	VERIFY(droptap_inited == 0);
99 	droptap_inited = 1;
100 
101 	LIST_INIT(&droptap_list);
102 
103 	error = if_clone_attach(&droptap_cloner);
104 	if (error != 0) {
105 		panic("%s: if_clone_attach() failed, error %d",
106 		    __func__, error);
107 	}
108 }
109 
110 int
droptap_clone_create(struct if_clone * ifc,u_int32_t unit,__unused void * params)111 droptap_clone_create(struct if_clone *ifc, u_int32_t unit, __unused void *params)
112 {
113 	int error = 0;
114 	struct droptap_softc *droptap = NULL;
115 	struct ifnet_init_eparams if_init;
116 
117 	droptap = kalloc_type(struct droptap_softc, Z_WAITOK_ZERO_NOFAIL);
118 	droptap->dtap_unit = unit;
119 
120 	bzero(&if_init, sizeof(if_init));
121 	if_init.ver = IFNET_INIT_CURRENT_VERSION;
122 	if_init.len = sizeof(if_init);
123 	if_init.flags = IFNET_INIT_LEGACY;
124 	if_init.name = __unsafe_null_terminated_from_indexable(ifc->ifc_name);
125 	if_init.unit = unit;
126 	if_init.type = IFT_OTHER;
127 	if_init.family = IFNET_FAMILY_LOOPBACK;
128 	if_init.output = droptap_if_output;
129 	if_init.add_proto = droptap_add_proto;
130 	if_init.del_proto = droptap_del_proto;
131 	if_init.softc = droptap;
132 	if_init.detach = droptap_detach;
133 
134 	error = ifnet_allocate_extended(&if_init, &droptap->dtap_ifp);
135 	if (error != 0) {
136 		printf("%s: ifnet_allocate_extended failed, error: %d\n",
137 		    __func__, error);
138 		goto done;
139 	}
140 
141 	ifnet_set_flags(droptap->dtap_ifp, IFF_UP, IFF_UP);
142 
143 	error = ifnet_attach(droptap->dtap_ifp, NULL);
144 	if (error != 0) {
145 		printf("%s: ifnet_attach failed, error: %d\n", __func__, error);
146 		ifnet_release(droptap->dtap_ifp);
147 		goto done;
148 	}
149 
150 	/* We use DLT_PKTAP for droptap as well. */
151 	bpf_attach(droptap->dtap_ifp, DLT_PKTAP, sizeof(struct droptap_header),
152 	    NULL, droptap_tap_callback);
153 
154 	ifnet_reference(droptap->dtap_ifp);
155 	lck_rw_lock_exclusive(&droptap_lck_rw);
156 	LIST_INSERT_HEAD(&droptap_list, droptap, dtap_link);
157 	lck_rw_done(&droptap_lck_rw);
158 done:
159 	if (error != 0 && droptap != NULL) {
160 		kfree_type(struct droptap_softc, droptap);
161 	}
162 	return error;
163 }
164 
165 int
droptap_clone_destroy(struct ifnet * ifp)166 droptap_clone_destroy(struct ifnet *ifp)
167 {
168 	int error = 0;
169 
170 	(void) ifnet_detach(ifp);
171 	return error;
172 }
173 
174 errno_t
droptap_tap_callback(ifnet_t ifp,u_int32_t dlt,bpf_tap_mode direction)175 droptap_tap_callback(ifnet_t ifp, u_int32_t dlt, bpf_tap_mode direction)
176 {
177 	struct droptap_softc *__single droptap;
178 
179 	droptap = ifp->if_softc;
180 	switch (dlt) {
181 	case DLT_PKTAP:
182 		if (direction == BPF_MODE_DISABLED) {
183 			if (droptap->dtap_dlt_pktap_count > 0) {
184 				droptap->dtap_dlt_pktap_count--;
185 				OSAddAtomic(-1, &droptap_total_tap_count);
186 			}
187 		} else {
188 			droptap->dtap_dlt_pktap_count++;
189 			OSAddAtomic(1, &droptap_total_tap_count);
190 		}
191 		break;
192 	}
193 	return 0;
194 }
195 
196 errno_t
droptap_if_output(ifnet_t __unused ifp,mbuf_t __unused m)197 droptap_if_output(ifnet_t __unused ifp, mbuf_t __unused m)
198 {
199 	return ENOTSUP;
200 }
201 
202 errno_t
droptap_add_proto(__unused ifnet_t ifp,__unused protocol_family_t pf,__unused const struct ifnet_demux_desc * dmx,__unused u_int32_t cnt)203 droptap_add_proto(__unused ifnet_t ifp, __unused protocol_family_t pf,
204     __unused const struct ifnet_demux_desc *dmx, __unused u_int32_t cnt)
205 {
206 	return 0;
207 }
208 
209 errno_t
droptap_del_proto(__unused ifnet_t ifp,__unused protocol_family_t pf)210 droptap_del_proto(__unused ifnet_t ifp, __unused protocol_family_t pf)
211 {
212 	return 0;
213 }
214 
215 void
droptap_detach(ifnet_t ifp)216 droptap_detach(ifnet_t ifp)
217 {
218 	struct droptap_softc *__single droptap;
219 
220 	lck_rw_lock_exclusive(&droptap_lck_rw);
221 
222 	droptap = ifp->if_softc;
223 	ifp->if_softc = NULL;
224 	LIST_REMOVE(droptap, dtap_link);
225 
226 	lck_rw_done(&droptap_lck_rw);
227 
228 	/* Drop reference as it's no more on the global list */
229 	ifnet_release(ifp);
230 
231 	kfree_type(struct droptap_softc, droptap);
232 	/* This is for the reference taken by ifnet_attach() */
233 	(void) ifnet_release(ifp);
234 }
235 
236 void
droptap_input_packet(kern_packet_t pkt,drop_reason_t reason,const char * funcname,uint16_t linenum,uint16_t flags,struct ifnet * ifp,pid_t pid,const char * pname,pid_t epid,const char * epname,uint8_t ipproto,uint32_t flowid)237 droptap_input_packet(kern_packet_t pkt, drop_reason_t reason,
238     const char *funcname, uint16_t linenum, uint16_t flags, struct ifnet *ifp,
239     pid_t pid, const char *pname, pid_t epid, const char *epname,
240     uint8_t ipproto, uint32_t flowid)
241 {
242 	struct droptap_header     dtaphdr;
243 	uint32_t                  dlt;
244 
245 	if (flags & DROPTAP_FLAG_L2_MISSING) {
246 		dlt = DLT_RAW;
247 	} else {
248 		dlt = DLT_EN10MB;
249 	}
250 	bzero(&dtaphdr, sizeof(struct droptap_header));
251 	dtaphdr.dth_dropreason = reason;
252 	if (funcname != NULL) {
253 		dtaphdr.dth_dropline = linenum;
254 		snprintf(dtaphdr.dth_dropfunc, sizeof(dtaphdr.dth_dropfunc), "%s", funcname);
255 		dtaphdr.dth_dropfunc_size = (uint8_t)strbuflen(dtaphdr.dth_dropfunc);
256 	}
257 
258 	droptap_bpf_tap_packet(pkt, DROPTAP_FLAG_DIR_IN | PTH_FLAG_NEXUS_CHAN,
259 	    &dtaphdr, dlt, ifp, pid, pname, epid, epname, ipproto, flowid);
260 }
261 
262 void
droptap_output_packet(kern_packet_t pkt,drop_reason_t reason,const char * funcname,uint16_t linenum,uint16_t flags,struct ifnet * ifp,pid_t pid,const char * pname,pid_t epid,const char * epname,uint8_t ipproto,uint32_t flowid)263 droptap_output_packet(kern_packet_t pkt, drop_reason_t reason,
264     const char *funcname, uint16_t linenum, uint16_t flags, struct ifnet *ifp,
265     pid_t pid, const char *pname, pid_t epid, const char *epname,
266     uint8_t ipproto, uint32_t flowid)
267 {
268 	struct droptap_header     dtaphdr;
269 	uint32_t                  dlt;
270 
271 	if (flags & DROPTAP_FLAG_L2_MISSING) {
272 		dlt = DLT_RAW;
273 	} else {
274 		dlt = DLT_EN10MB;
275 	}
276 	bzero(&dtaphdr, sizeof(struct droptap_header));
277 	dtaphdr.dth_dropreason = reason;
278 	if (funcname != NULL) {
279 		dtaphdr.dth_dropline = linenum;
280 		snprintf(dtaphdr.dth_dropfunc, sizeof(dtaphdr.dth_dropfunc), "%s", funcname);
281 		dtaphdr.dth_dropfunc_size = (uint8_t)strbuflen(dtaphdr.dth_dropfunc);
282 	}
283 
284 	droptap_bpf_tap_packet(pkt, DROPTAP_FLAG_DIR_OUT | PTH_FLAG_NEXUS_CHAN,
285 	    &dtaphdr, dlt, ifp, pid, pname, epid, epname, ipproto, flowid);
286 }
287 
288 static void
droptap_bpf_tap_packet(kern_packet_t pkt,uint32_t flags,struct droptap_header * dtaphdr,uint32_t dlt,struct ifnet * ifp,pid_t pid,const char * pname,pid_t epid,const char * epname,uint8_t ipproto,uint32_t flowid)289 droptap_bpf_tap_packet(kern_packet_t pkt, uint32_t flags,
290     struct droptap_header *dtaphdr, uint32_t dlt, struct ifnet *ifp, pid_t pid,
291     const char *pname, pid_t epid, const char *epname, uint8_t ipproto,
292     uint32_t flowid)
293 {
294 	struct droptap_softc *droptap;
295 	struct pktap_header *hdr;
296 	size_t hdr_size;
297 	void (*tap_packet_func)(ifnet_t, u_int32_t, kern_packet_t, void *, size_t) =
298 	    flags & DROPTAP_FLAG_DIR_OUT ? bpf_tap_packet_out : bpf_tap_packet_in;
299 
300 	hdr_size = DROPTAP_HDR_SIZE(dtaphdr);
301 
302 	hdr = (struct pktap_header *)dtaphdr;
303 	hdr->pth_length = sizeof(struct pktap_header);
304 	hdr->pth_type_next = PTH_TYPE_DROP;
305 	hdr->pth_dlt = dlt;
306 	hdr->pth_pid = pid;
307 	if (pid != epid) {
308 		hdr->pth_epid = epid;
309 	} else {
310 		hdr->pth_epid = -1;
311 	}
312 	if (pname != NULL) {
313 		strlcpy(hdr->pth_comm, pname, sizeof(hdr->pth_comm));
314 	}
315 	if (epname != NULL) {
316 		strlcpy(hdr->pth_ecomm, epname, sizeof(hdr->pth_ecomm));
317 	}
318 	if (ifp) {
319 		strlcpy(hdr->pth_ifname, ifp->if_xname, sizeof(hdr->pth_ifname));
320 		hdr->pth_iftype = ifp->if_type;
321 		hdr->pth_ifunit = ifp->if_unit;
322 	}
323 	hdr->pth_flags |= flags;
324 	hdr->pth_ipproto = ipproto;
325 	hdr->pth_flowid = flowid;
326 
327 	hdr->pth_flags |= flags & DROPTAP_FLAG_DIR_OUT ? PTH_FLAG_DIR_OUT : PTH_FLAG_DIR_IN;
328 	if ((flags & PTH_FLAG_SOCKET) != 0 && ipproto != 0 && flowid != 0) {
329 		hdr->pth_flags |= PTH_FLAG_DELAY_PKTAP;
330 	}
331 	if (kern_packet_get_wake_flag(pkt)) {
332 		hdr->pth_flags |= PTH_FLAG_WAKE_PKT;
333 	}
334 	/* Need to check the packet flag in case full wake has been requested */
335 	if (kern_packet_get_lpw_flag(pkt) || if_is_lpw_enabled(ifp)) {
336 		hdr->pth_flags |= PTH_FLAG_LPW;
337 	}
338 	hdr->pth_trace_tag = kern_packet_get_trace_tag(pkt);
339 	hdr->pth_svc = so_svc2tc((mbuf_svc_class_t)
340 	    kern_packet_get_service_class(pkt));
341 
342 	lck_rw_lock_shared(&droptap_lck_rw);
343 	LIST_FOREACH(droptap, &droptap_list, dtap_link) {
344 		if (droptap->dtap_dlt_pktap_count > 0) {
345 			tap_packet_func(droptap->dtap_ifp, DLT_PKTAP,
346 			    pkt, hdr, hdr_size);
347 		}
348 	}
349 	lck_rw_done(&droptap_lck_rw);
350 }
351 
352 void
droptap_input_mbuf(struct mbuf * m,drop_reason_t reason,const char * funcname,uint16_t linenum,uint16_t flags,struct ifnet * ifp,char * frame_header)353 droptap_input_mbuf(struct mbuf *m, drop_reason_t reason, const char *funcname,
354     uint16_t linenum, uint16_t flags, struct ifnet *ifp, char *frame_header)
355 {
356 	struct droptap_header    dtaphdr;
357 	char *hdr;
358 	char *start;
359 
360 	bzero(&dtaphdr, sizeof(struct droptap_header));
361 	dtaphdr.dth_dropreason = reason;
362 	if (funcname != NULL) {
363 		dtaphdr.dth_dropline = linenum;
364 		snprintf(dtaphdr.dth_dropfunc, sizeof(dtaphdr.dth_dropfunc), "%s", funcname);
365 		dtaphdr.dth_dropfunc_size = (uint8_t)strbuflen(dtaphdr.dth_dropfunc);
366 	}
367 
368 	hdr = mtod(m, char *);
369 	start = (char *)m_mtod_lower_bound(m);
370 	if (frame_header != NULL && frame_header >= start && frame_header <= hdr) {
371 		size_t o_len = m->m_len;
372 		u_int32_t pre = (u_int32_t)(hdr - frame_header);
373 
374 		if (mbuf_setdata(m, frame_header, o_len + pre) == 0) {
375 			droptap_bpf_tap_mbuf(m, DROPTAP_FLAG_DIR_IN | flags, &dtaphdr, ifp);
376 			mbuf_setdata(m, hdr, o_len);
377 		}
378 	} else {
379 		droptap_bpf_tap_mbuf(m, DROPTAP_FLAG_DIR_IN | flags, &dtaphdr, ifp);
380 	}
381 }
382 
383 void
droptap_output_mbuf(struct mbuf * m,drop_reason_t reason,const char * funcname,uint16_t linenum,uint16_t flags,struct ifnet * ifp)384 droptap_output_mbuf(struct mbuf *m, drop_reason_t reason, const char *funcname,
385     uint16_t linenum, uint16_t flags, struct ifnet *ifp)
386 {
387 	struct droptap_header    dtaphdr;
388 
389 	bzero(&dtaphdr, sizeof(struct droptap_header));
390 	dtaphdr.dth_dropreason = reason;
391 	if (funcname != NULL) {
392 		dtaphdr.dth_dropline = linenum;
393 		snprintf(dtaphdr.dth_dropfunc, sizeof(dtaphdr.dth_dropfunc), "%s", funcname);
394 		dtaphdr.dth_dropfunc_size = (uint8_t)strbuflen(dtaphdr.dth_dropfunc);
395 	}
396 
397 	droptap_bpf_tap_mbuf(m, DROPTAP_FLAG_DIR_OUT | flags, &dtaphdr, ifp);
398 }
399 
400 static void
droptap_bpf_tap_mbuf(struct mbuf * m,uint16_t flags,struct droptap_header * dtaphdr,struct ifnet * ifp)401 droptap_bpf_tap_mbuf(struct mbuf *m, uint16_t flags,
402     struct droptap_header *dtaphdr, struct ifnet *ifp)
403 {
404 	struct droptap_softc *droptap;
405 	struct pktap_header *hdr;
406 	size_t hdr_size;
407 	void (*bpf_tap_func)(ifnet_t, u_int32_t, mbuf_t, void *, size_t ) =
408 	    flags & DROPTAP_FLAG_DIR_OUT ? bpf_tap_out : bpf_tap_in;
409 
410 	hdr_size = DROPTAP_HDR_SIZE(dtaphdr);
411 
412 	hdr = (struct pktap_header *)dtaphdr;
413 	hdr->pth_length = sizeof(struct pktap_header);
414 	hdr->pth_type_next = PTH_TYPE_DROP;
415 
416 	/* Use DLT_RAW if L2 frame header is NULL */
417 	if (flags & DROPTAP_FLAG_L2_MISSING) {
418 		hdr->pth_dlt = DLT_RAW;
419 	} else {
420 		hdr->pth_dlt = DLT_EN10MB;
421 	}
422 
423 	hdr->pth_flags |= flags & DROPTAP_FLAG_DIR_OUT ? PTH_FLAG_DIR_OUT : PTH_FLAG_DIR_IN;
424 	if (ifp) {
425 		strlcpy(hdr->pth_ifname, ifp->if_xname, sizeof(hdr->pth_ifname));
426 		hdr->pth_iftype = ifp->if_type;
427 		hdr->pth_ifunit = ifp->if_unit;
428 	}
429 
430 	if (m->m_pkthdr.pkt_flags & PKTF_KEEPALIVE) {
431 		hdr->pth_flags |= PTH_FLAG_KEEP_ALIVE;
432 	}
433 	if (m->m_pkthdr.pkt_flags & PKTF_TCP_REXMT) {
434 		hdr->pth_flags |= PTH_FLAG_REXMIT;
435 	}
436 	if (m->m_pkthdr.pkt_flags & PKTF_WAKE_PKT) {
437 		hdr->pth_flags |= PTH_FLAG_WAKE_PKT;
438 	}
439 	if (m->m_pkthdr.pkt_ext_flags & PKTF_EXT_LPW || if_is_lpw_enabled(ifp)) {
440 		hdr->pth_flags |= PTH_FLAG_LPW;
441 	}
442 
443 	hdr->pth_svc = so_svc2tc(m->m_pkthdr.pkt_svc);
444 
445 	lck_rw_lock_exclusive(&droptap_lck_rw);
446 	LIST_FOREACH(droptap, &droptap_list, dtap_link) {
447 		if (droptap->dtap_dlt_pktap_count > 0) {
448 			bpf_tap_func(droptap->dtap_ifp, DLT_PKTAP,
449 			    m, hdr, hdr_size);
450 		}
451 	}
452 	lck_rw_done(&droptap_lck_rw);
453 }
454