xref: /xnu-11215.1.10/bsd/net/droptap.c (revision 8d741a5de7ff4191bf97d57b9f54c2f6d4a15585)
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_var_private.h>
34 #include <net/kpi_interface.h>
35 #include <net/pktap.h>
36 
37 
38 struct droptap_softc {
39 	LIST_ENTRY(droptap_softc)         dtap_link;
40 	uint32_t                          dtap_unit;
41 	uint32_t                          dtap_dlt_pktap_count;
42 	struct ifnet                      *dtap_ifp;
43 };
44 
45 static int droptap_inited = 0;
46 
47 SYSCTL_DECL(_net_link);
48 SYSCTL_NODE(_net_link, OID_AUTO, droptap,
49     CTLFLAG_RW  | CTLFLAG_LOCKED, 0, "droptap virtual interface");
50 
51 uint32_t droptap_total_tap_count = 0;
52 SYSCTL_UINT(_net_link_droptap, OID_AUTO, total_tap_count,
53     CTLFLAG_RD | CTLFLAG_LOCKED, &droptap_total_tap_count, 0, "");
54 
55 static LCK_GRP_DECLARE(droptap_lck_grp, "droptap");
56 static LCK_ATTR_DECLARE(droptap_lck_attr, 0, 0);
57 static LCK_RW_DECLARE_ATTR(droptap_lck_rw, &droptap_lck_grp, &droptap_lck_attr);
58 
59 
60 static LIST_HEAD(droptap_list, droptap_softc) droptap_list =
61     LIST_HEAD_INITIALIZER(droptap_list);
62 
63 int droptap_clone_create(struct if_clone *, u_int32_t, void *);
64 int droptap_clone_destroy(struct ifnet *);
65 
66 #define DROPTAP_MAXUNIT IF_MAXUNIT
67 
68 static struct if_clone droptap_cloner =
69     IF_CLONE_INITIALIZER(DROPTAP_IFNAME,
70     droptap_clone_create,
71     droptap_clone_destroy,
72     0,
73     DROPTAP_MAXUNIT);
74 
75 errno_t droptap_if_output(ifnet_t, mbuf_t);
76 errno_t droptap_add_proto(ifnet_t, protocol_family_t,
77     const struct ifnet_demux_desc *, u_int32_t);
78 errno_t droptap_del_proto(ifnet_t, protocol_family_t);
79 errno_t droptap_tap_callback(ifnet_t, u_int32_t, bpf_tap_mode);
80 void droptap_detach(ifnet_t);
81 static void droptap_bpf_tap_packet(kern_packet_t, uint32_t,
82     struct droptap_header *, uint32_t, struct ifnet *, pid_t,
83     const char *, pid_t, const char *, uint8_t, uint32_t);
84 static void droptap_bpf_tap_mbuf(struct mbuf *, uint16_t,
85     struct droptap_header *, struct ifnet *);
86 
87 
88 void
droptap_init(void)89 droptap_init(void)
90 {
91 	int error = 0;
92 
93 	VERIFY(droptap_inited == 0);
94 	droptap_inited = 1;
95 
96 	LIST_INIT(&droptap_list);
97 
98 	error = if_clone_attach(&droptap_cloner);
99 	if (error != 0) {
100 		panic("%s: if_clone_attach() failed, error %d",
101 		    __func__, error);
102 	}
103 }
104 
105 int
droptap_clone_create(struct if_clone * ifc,u_int32_t unit,__unused void * params)106 droptap_clone_create(struct if_clone *ifc, u_int32_t unit, __unused void *params)
107 {
108 	int error = 0;
109 	struct droptap_softc *droptap = NULL;
110 	struct ifnet_init_eparams if_init;
111 
112 	droptap = kalloc_type(struct droptap_softc, Z_WAITOK_ZERO_NOFAIL);
113 	droptap->dtap_unit = unit;
114 
115 	bzero(&if_init, sizeof(if_init));
116 	if_init.ver = IFNET_INIT_CURRENT_VERSION;
117 	if_init.len = sizeof(if_init);
118 	if_init.flags = IFNET_INIT_LEGACY;
119 	if_init.name = __unsafe_null_terminated_from_indexable(ifc->ifc_name);
120 	if_init.unit = unit;
121 	if_init.type = IFT_OTHER;
122 	if_init.family = IFNET_FAMILY_LOOPBACK;
123 	if_init.output = droptap_if_output;
124 	if_init.add_proto = droptap_add_proto;
125 	if_init.del_proto = droptap_del_proto;
126 	if_init.softc = droptap;
127 	if_init.detach = droptap_detach;
128 
129 	error = ifnet_allocate_extended(&if_init, &droptap->dtap_ifp);
130 	if (error != 0) {
131 		printf("%s: ifnet_allocate_extended failed, error: %d\n",
132 		    __func__, error);
133 		goto done;
134 	}
135 
136 	ifnet_set_flags(droptap->dtap_ifp, IFF_UP, IFF_UP);
137 
138 	error = ifnet_attach(droptap->dtap_ifp, NULL);
139 	if (error != 0) {
140 		printf("%s: ifnet_attach failed, error: %d\n", __func__, error);
141 		ifnet_release(droptap->dtap_ifp);
142 		goto done;
143 	}
144 
145 	/* We use DLT_PKTAP for droptap as well. */
146 	bpf_attach(droptap->dtap_ifp, DLT_PKTAP, sizeof(struct droptap_header),
147 	    NULL, droptap_tap_callback);
148 
149 	ifnet_reference(droptap->dtap_ifp);
150 	lck_rw_lock_exclusive(&droptap_lck_rw);
151 	LIST_INSERT_HEAD(&droptap_list, droptap, dtap_link);
152 	lck_rw_done(&droptap_lck_rw);
153 done:
154 	if (error != 0 && droptap != NULL) {
155 		kfree_type(struct droptap_softc, droptap);
156 	}
157 	return error;
158 }
159 
160 int
droptap_clone_destroy(struct ifnet * ifp)161 droptap_clone_destroy(struct ifnet *ifp)
162 {
163 	int error = 0;
164 
165 	(void) ifnet_detach(ifp);
166 	return error;
167 }
168 
169 errno_t
droptap_tap_callback(ifnet_t ifp,u_int32_t dlt,bpf_tap_mode direction)170 droptap_tap_callback(ifnet_t ifp, u_int32_t dlt, bpf_tap_mode direction)
171 {
172 	struct droptap_softc *__single droptap;
173 
174 	droptap = ifp->if_softc;
175 	switch (dlt) {
176 	case DLT_PKTAP:
177 		if (direction == BPF_MODE_DISABLED) {
178 			if (droptap->dtap_dlt_pktap_count > 0) {
179 				droptap->dtap_dlt_pktap_count--;
180 				OSAddAtomic(-1, &droptap_total_tap_count);
181 			}
182 		} else {
183 			droptap->dtap_dlt_pktap_count++;
184 			OSAddAtomic(1, &droptap_total_tap_count);
185 		}
186 		break;
187 	}
188 	return 0;
189 }
190 
191 errno_t
droptap_if_output(ifnet_t __unused ifp,mbuf_t __unused m)192 droptap_if_output(ifnet_t __unused ifp, mbuf_t __unused m)
193 {
194 	return ENOTSUP;
195 }
196 
197 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)198 droptap_add_proto(__unused ifnet_t ifp, __unused protocol_family_t pf,
199     __unused const struct ifnet_demux_desc *dmx, __unused u_int32_t cnt)
200 {
201 	return 0;
202 }
203 
204 errno_t
droptap_del_proto(__unused ifnet_t ifp,__unused protocol_family_t pf)205 droptap_del_proto(__unused ifnet_t ifp, __unused protocol_family_t pf)
206 {
207 	return 0;
208 }
209 
210 void
droptap_detach(ifnet_t ifp)211 droptap_detach(ifnet_t ifp)
212 {
213 	struct droptap_softc *__single droptap;
214 
215 	lck_rw_lock_exclusive(&droptap_lck_rw);
216 
217 	droptap = ifp->if_softc;
218 	ifp->if_softc = NULL;
219 	LIST_REMOVE(droptap, dtap_link);
220 
221 	lck_rw_done(&droptap_lck_rw);
222 
223 	/* Drop reference as it's no more on the global list */
224 	ifnet_release(ifp);
225 
226 	kfree_type(struct droptap_softc, droptap);
227 	/* This is for the reference taken by ifnet_attach() */
228 	(void) ifnet_release(ifp);
229 }
230 
231 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)232 droptap_input_packet(kern_packet_t pkt, drop_reason_t reason,
233     const char *funcname, uint16_t linenum, uint16_t flags, struct ifnet *ifp,
234     pid_t pid, const char *pname, pid_t epid, const char *epname,
235     uint8_t ipproto, uint32_t flowid)
236 {
237 	struct droptap_header     dtaphdr;
238 	uint32_t                  dlt;
239 
240 	if (flags & DROPTAP_FLAG_L2_MISSING) {
241 		dlt = DLT_RAW;
242 	} else {
243 		dlt = DLT_EN10MB;
244 	}
245 	bzero(&dtaphdr, sizeof(struct droptap_header));
246 	dtaphdr.dth_dropreason = reason;
247 	if (funcname != NULL) {
248 		dtaphdr.dth_dropline = linenum;
249 		snprintf(dtaphdr.dth_dropfunc, sizeof(dtaphdr.dth_dropfunc), "%s", funcname);
250 		dtaphdr.dth_dropfunc_size = (uint8_t)strbuflen(dtaphdr.dth_dropfunc);
251 	}
252 
253 	droptap_bpf_tap_packet(pkt, DROPTAP_FLAG_DIR_IN | PTH_FLAG_NEXUS_CHAN,
254 	    &dtaphdr, dlt, ifp, pid, pname, epid, epname, ipproto, flowid);
255 }
256 
257 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)258 droptap_output_packet(kern_packet_t pkt, drop_reason_t reason,
259     const char *funcname, uint16_t linenum, uint16_t flags, struct ifnet *ifp,
260     pid_t pid, const char *pname, pid_t epid, const char *epname,
261     uint8_t ipproto, uint32_t flowid)
262 {
263 	struct droptap_header     dtaphdr;
264 	uint32_t                  dlt;
265 
266 	if (flags & DROPTAP_FLAG_L2_MISSING) {
267 		dlt = DLT_RAW;
268 	} else {
269 		dlt = DLT_EN10MB;
270 	}
271 	bzero(&dtaphdr, sizeof(struct droptap_header));
272 	dtaphdr.dth_dropreason = reason;
273 	if (funcname != NULL) {
274 		dtaphdr.dth_dropline = linenum;
275 		snprintf(dtaphdr.dth_dropfunc, sizeof(dtaphdr.dth_dropfunc), "%s", funcname);
276 		dtaphdr.dth_dropfunc_size = (uint8_t)strbuflen(dtaphdr.dth_dropfunc);
277 	}
278 
279 	droptap_bpf_tap_packet(pkt, DROPTAP_FLAG_DIR_OUT | PTH_FLAG_NEXUS_CHAN,
280 	    &dtaphdr, dlt, ifp, pid, pname, epid, epname, ipproto, flowid);
281 }
282 
283 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)284 droptap_bpf_tap_packet(kern_packet_t pkt, uint32_t flags,
285     struct droptap_header *dtaphdr, uint32_t dlt, struct ifnet *ifp, pid_t pid,
286     const char *pname, pid_t epid, const char *epname, uint8_t ipproto,
287     uint32_t flowid)
288 {
289 	struct droptap_softc *droptap;
290 	struct pktap_header *hdr;
291 	size_t hdr_size;
292 	void (*tap_packet_func)(ifnet_t, u_int32_t, kern_packet_t, void *, size_t) =
293 	    flags & DROPTAP_FLAG_DIR_OUT ? bpf_tap_packet_out : bpf_tap_packet_in;
294 
295 	hdr_size = DROPTAP_HDR_SIZE(dtaphdr);
296 
297 	hdr = (struct pktap_header *)dtaphdr;
298 	hdr->pth_length = sizeof(struct pktap_header);
299 	hdr->pth_type_next = PTH_TYPE_DROP;
300 	hdr->pth_dlt = dlt;
301 	hdr->pth_pid = pid;
302 	if (pid != epid) {
303 		hdr->pth_epid = epid;
304 	} else {
305 		hdr->pth_epid = -1;
306 	}
307 	if (pname != NULL) {
308 		strlcpy(hdr->pth_comm, pname, sizeof(hdr->pth_comm));
309 	}
310 	if (epname != NULL) {
311 		strlcpy(hdr->pth_ecomm, epname, sizeof(hdr->pth_ecomm));
312 	}
313 	if (ifp) {
314 		strlcpy(hdr->pth_ifname, ifp->if_xname, sizeof(hdr->pth_ifname));
315 		hdr->pth_iftype = ifp->if_type;
316 		hdr->pth_ifunit = ifp->if_unit;
317 	}
318 	hdr->pth_flags |= flags;
319 	hdr->pth_ipproto = ipproto;
320 	hdr->pth_flowid = flowid;
321 
322 	hdr->pth_flags |= flags & DROPTAP_FLAG_DIR_OUT ? PTH_FLAG_DIR_OUT : PTH_FLAG_DIR_IN;
323 	if ((flags & PTH_FLAG_SOCKET) != 0 && ipproto != 0 && flowid != 0) {
324 		hdr->pth_flags |= PTH_FLAG_DELAY_PKTAP;
325 	}
326 	if (kern_packet_get_wake_flag(pkt)) {
327 		hdr->pth_flags |= PTH_FLAG_WAKE_PKT;
328 	}
329 	hdr->pth_trace_tag = kern_packet_get_trace_tag(pkt);
330 	hdr->pth_svc = so_svc2tc((mbuf_svc_class_t)
331 	    kern_packet_get_service_class(pkt));
332 
333 	lck_rw_lock_shared(&droptap_lck_rw);
334 	LIST_FOREACH(droptap, &droptap_list, dtap_link) {
335 		if (droptap->dtap_dlt_pktap_count > 0) {
336 			tap_packet_func(droptap->dtap_ifp, DLT_PKTAP,
337 			    pkt, hdr, hdr_size);
338 		}
339 	}
340 	lck_rw_done(&droptap_lck_rw);
341 }
342 
343 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)344 droptap_input_mbuf(struct mbuf *m, drop_reason_t reason, const char *funcname,
345     uint16_t linenum, uint16_t flags, struct ifnet *ifp, char *frame_header)
346 {
347 	struct droptap_header    dtaphdr;
348 	char *hdr;
349 	char *start;
350 
351 	bzero(&dtaphdr, sizeof(struct droptap_header));
352 	dtaphdr.dth_dropreason = reason;
353 	if (funcname != NULL) {
354 		dtaphdr.dth_dropline = linenum;
355 		snprintf(dtaphdr.dth_dropfunc, sizeof(dtaphdr.dth_dropfunc), "%s", funcname);
356 		dtaphdr.dth_dropfunc_size = (uint8_t)strbuflen(dtaphdr.dth_dropfunc);
357 	}
358 
359 	hdr = (char *)mbuf_data(m);
360 	start = (char *)mbuf_datastart(m);
361 	if (frame_header != NULL && frame_header >= start && frame_header <= hdr) {
362 		size_t o_len = m->m_len;
363 		u_int32_t pre = (u_int32_t)(hdr - frame_header);
364 
365 		if (mbuf_setdata(m, frame_header, o_len + pre) == 0) {
366 			droptap_bpf_tap_mbuf(m, DROPTAP_FLAG_DIR_IN | flags, &dtaphdr, ifp);
367 			mbuf_setdata(m, hdr, o_len);
368 		}
369 	} else {
370 		droptap_bpf_tap_mbuf(m, DROPTAP_FLAG_DIR_IN | flags, &dtaphdr, ifp);
371 	}
372 }
373 
374 void
droptap_output_mbuf(struct mbuf * m,drop_reason_t reason,const char * funcname,uint16_t linenum,uint16_t flags,struct ifnet * ifp)375 droptap_output_mbuf(struct mbuf *m, drop_reason_t reason, const char *funcname,
376     uint16_t linenum, uint16_t flags, struct ifnet *ifp)
377 {
378 	struct droptap_header    dtaphdr;
379 
380 	bzero(&dtaphdr, sizeof(struct droptap_header));
381 	dtaphdr.dth_dropreason = reason;
382 	if (funcname != NULL) {
383 		dtaphdr.dth_dropline = linenum;
384 		snprintf(dtaphdr.dth_dropfunc, sizeof(dtaphdr.dth_dropfunc), "%s", funcname);
385 		dtaphdr.dth_dropfunc_size = (uint8_t)strbuflen(dtaphdr.dth_dropfunc);
386 	}
387 
388 	droptap_bpf_tap_mbuf(m, DROPTAP_FLAG_DIR_OUT | flags, &dtaphdr, ifp);
389 }
390 
391 static void
droptap_bpf_tap_mbuf(struct mbuf * m,uint16_t flags,struct droptap_header * dtaphdr,struct ifnet * ifp)392 droptap_bpf_tap_mbuf(struct mbuf *m, uint16_t flags,
393     struct droptap_header *dtaphdr, struct ifnet *ifp)
394 {
395 	struct droptap_softc *droptap;
396 	struct pktap_header *hdr;
397 	size_t hdr_size;
398 	void (*bpf_tap_func)(ifnet_t, u_int32_t, mbuf_t, void *, size_t ) =
399 	    flags & DROPTAP_FLAG_DIR_OUT ? bpf_tap_out : bpf_tap_in;
400 
401 	hdr_size = DROPTAP_HDR_SIZE(dtaphdr);
402 
403 	hdr = (struct pktap_header *)dtaphdr;
404 	hdr->pth_length = sizeof(struct pktap_header);
405 	hdr->pth_type_next = PTH_TYPE_DROP;
406 
407 	/* Use DLT_RAW if L2 frame header is NULL */
408 	if (flags & DROPTAP_FLAG_L2_MISSING) {
409 		hdr->pth_dlt = DLT_RAW;
410 	} else {
411 		hdr->pth_dlt = DLT_EN10MB;
412 	}
413 
414 	hdr->pth_flags |= flags & DROPTAP_FLAG_DIR_OUT ? PTH_FLAG_DIR_OUT : PTH_FLAG_DIR_IN;
415 	if (ifp) {
416 		strlcpy(hdr->pth_ifname, ifp->if_xname, sizeof(hdr->pth_ifname));
417 		hdr->pth_iftype = ifp->if_type;
418 		hdr->pth_ifunit = ifp->if_unit;
419 	}
420 
421 	if (m->m_pkthdr.pkt_flags & PKTF_KEEPALIVE) {
422 		hdr->pth_flags |= PTH_FLAG_KEEP_ALIVE;
423 	}
424 	if (m->m_pkthdr.pkt_flags & PKTF_TCP_REXMT) {
425 		hdr->pth_flags |= PTH_FLAG_REXMIT;
426 	}
427 	if (m->m_pkthdr.pkt_flags & PKTF_WAKE_PKT) {
428 		hdr->pth_flags |= PTH_FLAG_WAKE_PKT;
429 	}
430 
431 	hdr->pth_svc = so_svc2tc(m->m_pkthdr.pkt_svc);
432 
433 	lck_rw_lock_exclusive(&droptap_lck_rw);
434 	LIST_FOREACH(droptap, &droptap_list, dtap_link) {
435 		if (droptap->dtap_dlt_pktap_count > 0) {
436 			bpf_tap_func(droptap->dtap_ifp, DLT_PKTAP,
437 			    m, hdr, hdr_size);
438 		}
439 	}
440 	lck_rw_done(&droptap_lck_rw);
441 }
442