]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet/ip_edgehole.c
xnu-1228.12.14.tar.gz
[apple/xnu.git] / bsd / netinet / ip_edgehole.c
1 #include <sys/param.h>
2 #include <sys/proc.h>
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
9 #include <net/if.h>
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"
25
26 enum
27 {
28 kEdgeHoleFlag_BlockInternet = 0x00000001,
29 kEdgeHoleFlag_BlockVV = 0x00000002
30 };
31
32 struct edgehole_tag
33 {
34 // flags tells us whether or not we should block traffic
35 u_int32_t eh_flags;
36
37 // These fields are used to help us find the PCB after we block traffic for TCP
38 struct inpcbinfo *eh_inpinfo;
39 struct inpcb *eh_inp;
40 };
41
42 struct edgehole_delayed_notify
43 {
44 // flags tells us whether or not we should block traffic
45 struct edgehole_delayed_notify *next;
46
47 // These fields are used to help us find the PCB after we block traffic for TCP
48 struct inpcbinfo *inpinfo;
49 struct inpcb *inp;
50 };
51
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;
56
57 #ifndef HAS_COMPARE_AND_SWAP_PTR
58 // 64bit kernels have an OSCompareAndSwapPtr that does the right thing
59 static Boolean
60 OSCompareAndSwapPtr(
61 void *oldValue,
62 void *newValue,
63 volatile void *address)
64 {
65 return OSCompareAndSwap((UInt32)oldValue, (UInt32)newValue, (volatile UInt32*)address);
66 }
67 #endif
68
69 static void
70 ip_edgehole_notify_delayed(
71 struct inpcb *inp,
72 struct inpcbinfo *inpinfo)
73 {
74 if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) != WNT_STOPUSING)
75 {
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)
79 {
80 socket_lock(so, 1);
81 if (in_pcb_checkstate(inp, WNT_RELEASE,1) != WNT_STOPUSING)
82 {
83 if (inp->inp_ip_p == IPPROTO_TCP)
84 {
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.
89 union
90 {
91 caddr_t caddrt_sucks;
92 void *void_ptr;
93 } bite_me;
94
95 bite_me.caddrt_sucks = inp->inp_ppcb;
96 tcp_drop((struct tcpcb*)bite_me.void_ptr, EPERM);
97 }
98 else
99 {
100 // Is this enough?
101 socantsendmore(so);
102 }
103 }
104 socket_unlock(so, 1);
105 }
106 }
107 }
108
109 // Some shortcomings of this strategy:
110 // 1) an inpcb could be reused for a new socket before we get a chance to notify
111
112 static void
113 ip_edgehole_process_delayed(
114 __unused void *unused1,
115 __unused void *unused2)
116 {
117 struct edgehole_delayed_notify *head;
118
119 while (edgehole_delay_list)
120 {
121 // Atomically grab the list
122 do
123 {
124 head = edgehole_delay_list;
125 }
126 while (!OSCompareAndSwapPtr(head, NULL, &edgehole_delay_list));
127
128 if (head == NULL)
129 {
130 break;
131 }
132
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)
138 {
139 current_p = &head;
140 while (*current_p)
141 {
142 if ((*current_p)->inp == current->inp)
143 {
144 ye_dead = *current_p;
145 *current_p = ye_dead->next;
146 OSFree(ye_dead, sizeof(*ye_dead), edgehole_mtag);
147 }
148 else
149 {
150 current_p = &(*current_p)->next;
151 }
152 }
153 }
154
155 while (head)
156 {
157 struct inpcbinfo *lockedinfo;
158
159 lockedinfo = head->inpinfo;
160
161 // Lock the list
162 lck_rw_lock_shared(lockedinfo->mtx);
163
164 struct inpcb *inp;
165
166 // Walk the inp list.
167 LIST_FOREACH(inp, lockedinfo->listhead, inp_list)
168 {
169 // Walk the list of notifications
170 for (current = head; current != NULL; current = current->next)
171 {
172 // Found a match, notify
173 if (current->inpinfo == lockedinfo && current->inp == inp)
174 {
175 ip_edgehole_notify_delayed(inp, lockedinfo);
176 }
177 }
178 }
179
180 lck_rw_done(lockedinfo->mtx);
181
182 // Release all the notifications for this inpcbinfo
183 current_p = &head;
184 while (*current_p)
185 {
186 // Free any items for this inpcbinfo
187 if ((*current_p)->inpinfo == lockedinfo)
188 {
189 ye_dead = *current_p;
190 *current_p = ye_dead->next;
191 OSFree(ye_dead, sizeof(*ye_dead), edgehole_mtag);
192 }
193 else
194 {
195 current_p = &(*current_p)->next;
196 }
197 }
198 }
199 }
200 }
201
202 static void
203 ip_edgehole_notify(
204 struct edgehole_tag *tag)
205 {
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.
209
210 if (tag->eh_inp == NULL || tag->eh_inpinfo == NULL)
211 return;
212
213 struct edgehole_delayed_notify *delayed = OSMalloc(sizeof(*delayed), edgehole_mtag);
214 if (delayed)
215 {
216 delayed->inp = tag->eh_inp;
217 delayed->inpinfo = tag->eh_inpinfo;
218 do
219 {
220 delayed->next = edgehole_delay_list;
221 }
222 while (!OSCompareAndSwapPtr(delayed->next, delayed, &edgehole_delay_list));
223
224 thread_call_enter(edgehole_callout);
225 }
226 }
227
228 __private_extern__ void
229 ip_edgehole_attach(
230 struct inpcb *inp)
231 {
232 inp->inpcb_edgehole_flags = 0;
233 inp->inpcb_edgehole_mask = 0;
234
235 // TBD: call MAC framework to find out of we are allowed to use EDGE
236 #ifdef TEST_THE_EVIL_EDGE_HOLE
237 char pidname[64];
238 proc_selfname(pidname, sizeof(pidname));
239 pidname[sizeof(pidname) -1] = 0;
240 if (strcmp(pidname, "MobileSafari") == 0 ||
241 strcmp(pidname, "ping") == 0)
242 {
243 inp->inpcb_edgehole_flags = kEdgeHoleFlag_BlockInternet;
244 inp->inpcb_edgehole_mask = kEdgeHoleFlag_BlockInternet;
245 }
246 #endif
247
248 if (inp->inpcb_edgehole_mask != 0)
249 {
250 // Allocate a callout
251 if (edgehole_callout == NULL)
252 {
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);
257 }
258
259 // Allocate a malloc tag
260 if (edgehole_mtag == 0)
261 {
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);
266 }
267 }
268 }
269
270 __private_extern__ void
271 ip_edgehole_mbuf_tag(
272 struct inpcb *inp,
273 mbuf_t m)
274 {
275 // Immediately bail if there are no flags on this inpcb
276 if (inp->inpcb_edgehole_mask == 0)
277 {
278 return;
279 }
280
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);
284
285 struct edgehole_tag *tag;
286 size_t length;
287
288 // Find an existing tag
289 if (mbuf_tag_find(m, edgehole_tag, 0, &length, (void**)&tag) == 0)
290 {
291 if (length != sizeof(*tag))
292 panic("ip_edgehole_mbuf_tag - existing tag is wrong size");
293
294 // add restrictions
295 tag->eh_flags = (tag->eh_flags & (~inp->inpcb_edgehole_mask)) |
296 (inp->inpcb_edgehole_flags & inp->inpcb_edgehole_mask);
297 }
298 else if ((inp->inpcb_edgehole_mask & inp->inpcb_edgehole_flags) != 0)
299 {
300 // Add the tag
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?
303
304 tag->eh_flags = (inp->inpcb_edgehole_flags & inp->inpcb_edgehole_mask);
305 tag->eh_inp = inp;
306 tag->eh_inpinfo = inp->inp_pcbinfo;
307 }
308 }
309
310 int
311 ip_edgehole_filter(
312 mbuf_t *m,
313 __unused int isVV)
314 {
315 struct edgehole_tag *tag;
316 size_t length;
317
318 if (mbuf_tag_find(*m, edgehole_tag, 0, &length, (void**)&tag) == 0)
319 {
320 if (length != sizeof(*tag))
321 panic("ip_edgehole_filter - existing tag is wrong size");
322
323 if ((tag->eh_flags & kEdgeHoleFlag_BlockInternet) != 0)
324 {
325 ip_edgehole_notify(tag);
326
327 mbuf_freem(*m); *m = NULL;
328 return EPERM;
329 }
330 }
331
332 return 0;
333 }