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