3 #include <sys/kpi_mbuf.h>
4 #include <sys/socket.h>
5 #include <sys/socketvar.h>
6 #include <string.h> // For bzero
7 #include <libkern/libkern.h> // for printf
8 #include <kern/debug.h> // For panic
10 #include <net/if_types.h>
11 #include <net/route.h>
12 #include <netinet/in.h>
13 #include <netinet/in_pcb.h>
14 #include <netinet/in_var.h>
15 #include <netinet/ip_var.h>
16 #include <netinet/tcp.h>
17 #include <netinet/tcp_fsm.h>
18 #include <netinet/tcp_seq.h>
19 #include <netinet/tcp_timer.h>
20 #include <netinet/tcp_var.h>
21 #include <libkern/OSMalloc.h>
22 #include <libkern/OSAtomic.h>
23 #include <kern/thread_call.h>
24 #include "ip_edgehole.h"
28 kEdgeHoleFlag_BlockInternet
= 0x00000001,
29 kEdgeHoleFlag_BlockVV
= 0x00000002
34 // flags tells us whether or not we should block traffic
37 // These fields are used to help us find the PCB after we block traffic for TCP
38 struct inpcbinfo
*eh_inpinfo
;
42 struct edgehole_delayed_notify
44 // flags tells us whether or not we should block traffic
45 struct edgehole_delayed_notify
*next
;
47 // These fields are used to help us find the PCB after we block traffic for TCP
48 struct inpcbinfo
*inpinfo
;
52 static mbuf_tag_id_t edgehole_tag
= 0;
53 static thread_call_t edgehole_callout
= NULL
;
54 static OSMallocTag edgehole_mtag
= 0;
55 static struct edgehole_delayed_notify
*edgehole_delay_list
= NULL
;
57 #ifndef HAS_COMPARE_AND_SWAP_PTR
58 // 64bit kernels have an OSCompareAndSwapPtr that does the right thing
63 volatile void *address
)
65 return OSCompareAndSwap((UInt32
)oldValue
, (UInt32
)newValue
, (volatile UInt32
*)address
);
70 ip_edgehole_notify_delayed(
72 struct inpcbinfo
*inpinfo
)
74 if (in_pcb_checkstate(inp
, WNT_ACQUIRE
, 0) != WNT_STOPUSING
)
76 // We've found an inpcb for the packet we're dropping.
77 struct socket
*so
= inp
->inp_socket
;
78 if (so
&& so
!= &inpinfo
->nat_dummy_socket
)
81 if (in_pcb_checkstate(inp
, WNT_RELEASE
,1) != WNT_STOPUSING
)
83 if (inp
->inp_ip_p
== IPPROTO_TCP
)
85 // Why do we still have caddr_t? Come on! Casting from
86 // caddr_t to something else causes "cast increases required alignment"
87 // warnings. warnings are treated as failures. This union does the
88 // exact same thing without the warning.
95 bite_me
.caddrt_sucks
= inp
->inp_ppcb
;
96 tcp_drop((struct tcpcb
*)bite_me
.void_ptr
, EPERM
);
104 socket_unlock(so
, 1);
109 // Some shortcomings of this strategy:
110 // 1) an inpcb could be reused for a new socket before we get a chance to notify
113 ip_edgehole_process_delayed(
114 __unused
void *unused1
,
115 __unused
void *unused2
)
117 struct edgehole_delayed_notify
*head
;
119 while (edgehole_delay_list
)
121 // Atomically grab the list
124 head
= edgehole_delay_list
;
126 while (!OSCompareAndSwapPtr(head
, NULL
, &edgehole_delay_list
));
133 // Prune duplicates from the list
134 struct edgehole_delayed_notify
*current
;
135 struct edgehole_delayed_notify
**current_p
;
136 struct edgehole_delayed_notify
*ye_dead
;
137 for (current
= head
; current
&& current
->next
; current
= current
->next
)
142 if ((*current_p
)->inp
== current
->inp
)
144 ye_dead
= *current_p
;
145 *current_p
= ye_dead
->next
;
146 OSFree(ye_dead
, sizeof(*ye_dead
), edgehole_mtag
);
150 current_p
= &(*current_p
)->next
;
157 struct inpcbinfo
*lockedinfo
;
159 lockedinfo
= head
->inpinfo
;
162 lck_rw_lock_shared(lockedinfo
->mtx
);
166 // Walk the inp list.
167 LIST_FOREACH(inp
, lockedinfo
->listhead
, inp_list
)
169 // Walk the list of notifications
170 for (current
= head
; current
!= NULL
; current
= current
->next
)
172 // Found a match, notify
173 if (current
->inpinfo
== lockedinfo
&& current
->inp
== inp
)
175 ip_edgehole_notify_delayed(inp
, lockedinfo
);
180 lck_rw_done(lockedinfo
->mtx
);
182 // Release all the notifications for this inpcbinfo
186 // Free any items for this inpcbinfo
187 if ((*current_p
)->inpinfo
== lockedinfo
)
189 ye_dead
= *current_p
;
190 *current_p
= ye_dead
->next
;
191 OSFree(ye_dead
, sizeof(*ye_dead
), edgehole_mtag
);
195 current_p
= &(*current_p
)->next
;
204 struct edgehole_tag
*tag
)
206 // Since the lock on the socket may be held while a packet is being transmitted,
207 // we must allocate storage to keep track of this information and schedule a
208 // thread to handle the work.
210 if (tag
->eh_inp
== NULL
|| tag
->eh_inpinfo
== NULL
)
213 struct edgehole_delayed_notify
*delayed
= OSMalloc(sizeof(*delayed
), edgehole_mtag
);
216 delayed
->inp
= tag
->eh_inp
;
217 delayed
->inpinfo
= tag
->eh_inpinfo
;
220 delayed
->next
= edgehole_delay_list
;
222 while (!OSCompareAndSwapPtr(delayed
->next
, delayed
, &edgehole_delay_list
));
224 thread_call_enter(edgehole_callout
);
228 __private_extern__
void
232 inp
->inpcb_edgehole_flags
= 0;
233 inp
->inpcb_edgehole_mask
= 0;
235 // TBD: call MAC framework to find out of we are allowed to use EDGE
236 #ifdef TEST_THE_EVIL_EDGE_HOLE
238 proc_selfname(pidname
, sizeof(pidname
));
239 pidname
[sizeof(pidname
) -1] = 0;
240 if (strcmp(pidname
, "MobileSafari") == 0 ||
241 strcmp(pidname
, "ping") == 0)
243 inp
->inpcb_edgehole_flags
= kEdgeHoleFlag_BlockInternet
;
244 inp
->inpcb_edgehole_mask
= kEdgeHoleFlag_BlockInternet
;
248 if (inp
->inpcb_edgehole_mask
!= 0)
250 // Allocate a callout
251 if (edgehole_callout
== NULL
)
253 thread_call_t tmp_callout
= thread_call_allocate(ip_edgehole_process_delayed
, NULL
);
254 if (!tmp_callout
) panic("ip_edgehole_attach: thread_call_allocate failed");
255 if (!OSCompareAndSwapPtr(NULL
, tmp_callout
, &edgehole_callout
))
256 thread_call_free(tmp_callout
);
259 // Allocate a malloc tag
260 if (edgehole_mtag
== 0)
262 OSMallocTag mtag
= OSMalloc_Tagalloc("com.apple.ip_edgehole", 0);
263 if (!mtag
) panic("ip_edgehole_attach: OSMalloc_Tagalloc failed");
264 if (!OSCompareAndSwapPtr(NULL
, mtag
, &edgehole_mtag
))
265 OSMalloc_Tagfree(mtag
);
270 __private_extern__
void
271 ip_edgehole_mbuf_tag(
275 // Immediately bail if there are no flags on this inpcb
276 if (inp
->inpcb_edgehole_mask
== 0)
281 // Allocate a tag_id if we don't have one already
282 if (edgehole_tag
== 0)
283 mbuf_tag_id_find("com.apple.edgehole", &edgehole_tag
);
285 struct edgehole_tag
*tag
;
288 // Find an existing tag
289 if (mbuf_tag_find(m
, edgehole_tag
, 0, &length
, (void**)&tag
) == 0)
291 if (length
!= sizeof(*tag
))
292 panic("ip_edgehole_mbuf_tag - existing tag is wrong size");
295 tag
->eh_flags
= (tag
->eh_flags
& (~inp
->inpcb_edgehole_mask
)) |
296 (inp
->inpcb_edgehole_flags
& inp
->inpcb_edgehole_mask
);
298 else if ((inp
->inpcb_edgehole_mask
& inp
->inpcb_edgehole_flags
) != 0)
301 if (mbuf_tag_allocate(m
, edgehole_tag
, 0, sizeof(*tag
), MBUF_WAITOK
, (void**)&tag
) != 0)
302 panic("ip_edgehole_mbuf_tag - mbuf_tag_allocate failed"); // ouch - how important is it that we block this stuff?
304 tag
->eh_flags
= (inp
->inpcb_edgehole_flags
& inp
->inpcb_edgehole_mask
);
306 tag
->eh_inpinfo
= inp
->inp_pcbinfo
;
315 struct edgehole_tag
*tag
;
318 if (mbuf_tag_find(*m
, edgehole_tag
, 0, &length
, (void**)&tag
) == 0)
320 if (length
!= sizeof(*tag
))
321 panic("ip_edgehole_filter - existing tag is wrong size");
323 if ((tag
->eh_flags
& kEdgeHoleFlag_BlockInternet
) != 0)
325 ip_edgehole_notify(tag
);
327 mbuf_freem(*m
); *m
= NULL
;