]>
Commit | Line | Data |
---|---|---|
fe8ab488 | 1 | /* |
39037602 | 2 | * Copyright (c) 2015-2016 Apple Inc. All rights reserved. |
fe8ab488 A |
3 | * |
4 | * @APPLE_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. Please obtain a copy of the License at | |
10 | * http: www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * THEORY OF OPERATION | |
26 | * | |
27 | * The packet mangler subsystem provides a limited way for user space | |
28 | * applications to apply certain actions on certain flows. | |
29 | * | |
30 | * A user space applications opens a kernel control socket with the name | |
31 | * PACKET_MANGLER_CONTROL_NAME to attach to the packet mangler subsystem. | |
32 | * When connected, a "struct packet_mangler" is created and set as the | |
33 | * "unitinfo" of the corresponding kernel control socket instance. | |
34 | * Connect call for packet mangler's kernel control socket also registers | |
35 | * ip filers with cookie set to the packet_mangler instance. | |
36 | * The ip filters are removed when control socket is disconnected. | |
37 | */ | |
38 | #include <sys/types.h> | |
39 | #include <sys/kern_control.h> | |
40 | #include <sys/domain.h> | |
41 | #include <sys/protosw.h> | |
42 | #include <sys/syslog.h> | |
43 | ||
44 | #include <kern/locks.h> | |
45 | #include <kern/zalloc.h> | |
46 | #include <kern/debug.h> | |
47 | ||
48 | #include <net/packet_mangler.h> | |
49 | ||
50 | #include <netinet/tcp.h> | |
51 | #include <netinet/tcp_var.h> | |
52 | #include <netinet/ip.h> | |
cb323159 | 53 | #include <netinet/ip6.h> |
fe8ab488 A |
54 | #include <netinet/kpi_ipfilter.h> |
55 | #include <string.h> | |
56 | #include <libkern/libkern.h> | |
57 | ||
0a7de745 | 58 | #define MAX_PACKET_MANGLER 1 |
fe8ab488 | 59 | |
0a7de745 | 60 | #define PKT_MNGLR_FLG_IPFILTER_ATTACHED 0x00000001 |
fe8ab488 | 61 | |
0a7de745 A |
62 | SYSCTL_NODE(_net, OID_AUTO, pktmnglr, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "pktmnglr"); |
63 | SYSCTL_INT(_net_pktmnglr, OID_AUTO, log, CTLFLAG_RW | CTLFLAG_LOCKED, | |
64 | &pkt_mnglr_log_level, 0, ""); | |
fe8ab488 A |
65 | /* |
66 | * The structure packet_mangler represents a user space packet filter | |
67 | * It's created and associated with a kernel control socket instance | |
68 | */ | |
69 | struct packet_mangler { | |
0a7de745 A |
70 | kern_ctl_ref pkt_mnglr_kcref; |
71 | uint32_t pkt_mnglr_kcunit; | |
72 | uint32_t pkt_mnglr_flags; | |
fe8ab488 | 73 | /* IP filter related params */ |
0a7de745 A |
74 | ipfilter_t pkt_mnglr_ipfref; |
75 | ipfilter_t pkt_mnglr_ipfrefv6; | |
76 | struct ipf_filter pkt_mnglr_ipfilter; | |
fe8ab488 A |
77 | |
78 | /* Options */ | |
0a7de745 A |
79 | uint8_t activate; |
80 | Pkt_Mnglr_Flow dir; | |
81 | struct sockaddr_storage lsaddr; | |
82 | struct sockaddr_storage rsaddr; | |
83 | struct sockaddr_storage swap_lsaddr; | |
84 | struct sockaddr_storage swap_rsaddr; | |
85 | uint32_t ip_action_mask; | |
86 | uint16_t lport; | |
87 | uint16_t rport; | |
88 | uint32_t proto; | |
89 | uint32_t proto_action_mask; | |
fe8ab488 A |
90 | }; |
91 | ||
92 | /* Array of all the packet mangler instancesi */ | |
93 | struct packet_mangler **packet_manglers = NULL; | |
94 | ||
0a7de745 | 95 | uint32_t pkt_mnglr_active_count = 0; /* Number of active packet filters */ |
fe8ab488 A |
96 | uint32_t pkt_mnglr_close_wait_timeout = 1000; /* in milliseconds */ |
97 | ||
98 | static kern_ctl_ref pkt_mnglr_kctlref = NULL; | |
99 | ||
100 | static lck_grp_attr_t *pkt_mnglr_lck_grp_attr = NULL; | |
101 | static lck_attr_t *pkt_mnglr_lck_attr = NULL; | |
102 | static lck_grp_t *pkt_mnglr_lck_grp = NULL; | |
103 | ||
104 | /* The lock below protects packet_manglers DS, packet_mangler DS */ | |
105 | decl_lck_rw_data(static, pkt_mnglr_lck_rw); | |
106 | ||
0a7de745 | 107 | #define PKT_MNGLR_RW_LCK_MAX 8 |
fe8ab488 A |
108 | |
109 | int pkt_mnglr_rw_nxt_lck = 0; | |
110 | void* pkt_mnglr_rw_lock_history[PKT_MNGLR_RW_LCK_MAX]; | |
111 | ||
112 | int pkt_mnglr_rw_nxt_unlck = 0; | |
113 | void* pkt_mnglr_rw_unlock_history[PKT_MNGLR_RW_LCK_MAX]; | |
114 | ||
115 | ||
0a7de745 A |
116 | #define PACKET_MANGLER_ZONE_NAME "packet_mangler" |
117 | #define PACKET_MANGLER_ZONE_MAX 10 | |
118 | static struct zone *packet_mangler_zone = NULL; /* zone for packet_mangler */ | |
fe8ab488 A |
119 | |
120 | /* | |
121 | * For troubleshooting | |
122 | */ | |
123 | int pkt_mnglr_log_level = LOG_ERR; | |
124 | int pkt_mnglr_debug = 1; | |
125 | ||
126 | /* | |
127 | * Forward declaration to appease the compiler | |
128 | */ | |
129 | static void pkt_mnglr_rw_lock_exclusive(lck_rw_t *); | |
130 | static void pkt_mnglr_rw_unlock_exclusive(lck_rw_t *); | |
131 | static void pkt_mnglr_rw_lock_shared(lck_rw_t *); | |
132 | static void pkt_mnglr_rw_unlock_shared(lck_rw_t *); | |
133 | ||
134 | static errno_t pktmnglr_ipfilter_output(void *cookie, mbuf_t *data, | |
0a7de745 | 135 | ipf_pktopts_t options); |
fe8ab488 | 136 | static errno_t pktmnglr_ipfilter_input(void *cookie, mbuf_t *data, |
0a7de745 | 137 | int offset, u_int8_t protocol); |
fe8ab488 A |
138 | static void pktmnglr_ipfilter_detach(void *cookie); |
139 | ||
140 | static void chksm_update(mbuf_t data); | |
141 | ||
0a7de745 A |
142 | #define TCP_OPT_MULTIPATH_TCP 30 |
143 | #define MPTCP_SBT_VER_OFFSET 2 | |
3e170ce0 | 144 | |
0a7de745 A |
145 | #define MPTCP_SUBTYPE_MPCAPABLE 0x0 |
146 | #define MPTCP_SUBTYPE_MPJOIN 0x1 | |
147 | #define MPTCP_SUBTYPE_DSS 0x2 | |
148 | #define MPTCP_SUBTYPE_ADD_ADDR 0x3 | |
149 | #define MPTCP_SUBTYPE_REM_ADDR 0x4 | |
150 | #define MPTCP_SUBTYPE_MP_PRIO 0x5 | |
151 | #define MPTCP_SUBTYPE_MP_FAIL 0x6 | |
152 | #define MPTCP_SUBTYPE_MP_FASTCLOSE 0x7 | |
3e170ce0 | 153 | |
fe8ab488 A |
154 | /* |
155 | * packet filter global read write lock | |
156 | */ | |
157 | ||
158 | static void | |
159 | pkt_mnglr_rw_lock_exclusive(lck_rw_t *lck) | |
160 | { | |
161 | void *lr_saved; | |
162 | ||
163 | lr_saved = __builtin_return_address(0); | |
164 | ||
165 | lck_rw_lock_exclusive(lck); | |
166 | ||
167 | pkt_mnglr_rw_lock_history[pkt_mnglr_rw_nxt_lck] = lr_saved; | |
168 | pkt_mnglr_rw_nxt_lck = | |
169 | (pkt_mnglr_rw_nxt_lck + 1) % PKT_MNGLR_RW_LCK_MAX; | |
170 | } | |
171 | ||
172 | static void | |
173 | pkt_mnglr_rw_unlock_exclusive(lck_rw_t *lck) | |
174 | { | |
175 | void *lr_saved; | |
176 | ||
177 | lr_saved = __builtin_return_address(0); | |
178 | ||
179 | lck_rw_unlock_exclusive(lck); | |
180 | ||
181 | pkt_mnglr_rw_unlock_history[pkt_mnglr_rw_nxt_unlck] = | |
182 | lr_saved; | |
183 | pkt_mnglr_rw_nxt_unlck = (pkt_mnglr_rw_nxt_unlck + 1) % PKT_MNGLR_RW_LCK_MAX; | |
184 | } | |
185 | ||
186 | static void | |
187 | pkt_mnglr_rw_lock_shared(lck_rw_t *lck) | |
188 | { | |
189 | void *lr_saved; | |
190 | ||
191 | lr_saved = __builtin_return_address(0); | |
192 | ||
193 | lck_rw_lock_shared(lck); | |
194 | ||
195 | pkt_mnglr_rw_lock_history[pkt_mnglr_rw_nxt_lck] = lr_saved; | |
196 | pkt_mnglr_rw_nxt_lck = (pkt_mnglr_rw_nxt_lck + 1) % PKT_MNGLR_RW_LCK_MAX; | |
197 | } | |
198 | ||
199 | static void | |
200 | pkt_mnglr_rw_unlock_shared(lck_rw_t *lck) | |
201 | { | |
202 | void *lr_saved; | |
203 | ||
204 | lr_saved = __builtin_return_address(0); | |
205 | ||
206 | lck_rw_unlock_shared(lck); | |
207 | ||
208 | pkt_mnglr_rw_unlock_history[pkt_mnglr_rw_nxt_unlck] = lr_saved; | |
209 | pkt_mnglr_rw_nxt_unlck = (pkt_mnglr_rw_nxt_unlck + 1) % PKT_MNGLR_RW_LCK_MAX; | |
210 | } | |
211 | ||
212 | /* | |
213 | * Packet Mangler's Kernel control socket callbacks | |
214 | */ | |
215 | static errno_t | |
216 | pkt_mnglr_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac, | |
0a7de745 | 217 | void **unitinfo) |
fe8ab488 | 218 | { |
0a7de745 | 219 | errno_t error = 0; |
fe8ab488 A |
220 | struct packet_mangler *p_pkt_mnglr = NULL; |
221 | ||
222 | PKT_MNGLR_LOG(LOG_NOTICE, "Connecting packet mangler filter."); | |
223 | ||
224 | p_pkt_mnglr = zalloc(packet_mangler_zone); | |
225 | if (p_pkt_mnglr == NULL) { | |
226 | PKT_MNGLR_LOG(LOG_ERR, "zalloc failed"); | |
227 | error = ENOMEM; | |
228 | goto done; | |
229 | } | |
230 | ||
231 | bzero(p_pkt_mnglr, sizeof(struct packet_mangler)); | |
232 | ||
233 | pkt_mnglr_rw_lock_exclusive(&pkt_mnglr_lck_rw); | |
234 | if (packet_manglers == NULL) { | |
235 | struct packet_mangler **tmp; | |
236 | ||
237 | pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw); | |
238 | ||
239 | MALLOC(tmp, | |
240 | struct packet_mangler **, | |
241 | MAX_PACKET_MANGLER * sizeof(struct packet_mangler *), | |
242 | M_TEMP, | |
243 | M_WAITOK | M_ZERO); | |
244 | ||
245 | pkt_mnglr_rw_lock_exclusive(&pkt_mnglr_lck_rw); | |
246 | ||
247 | if (tmp == NULL && packet_manglers == NULL) { | |
248 | error = ENOMEM; | |
249 | pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw); | |
250 | goto done; | |
251 | } | |
252 | /* Another thread may have won the race */ | |
0a7de745 | 253 | if (packet_manglers != NULL) { |
fe8ab488 | 254 | FREE(tmp, M_TEMP); |
0a7de745 | 255 | } else { |
fe8ab488 | 256 | packet_manglers = tmp; |
0a7de745 | 257 | } |
fe8ab488 A |
258 | } |
259 | ||
260 | if (sac->sc_unit == 0 || sac->sc_unit > MAX_PACKET_MANGLER) { | |
261 | PKT_MNGLR_LOG(LOG_ERR, "bad sc_unit %u", sac->sc_unit); | |
262 | error = EINVAL; | |
263 | } else if (packet_manglers[sac->sc_unit - 1] != NULL) { | |
264 | PKT_MNGLR_LOG(LOG_ERR, "sc_unit %u in use", sac->sc_unit); | |
265 | error = EADDRINUSE; | |
266 | } else { | |
267 | /* | |
268 | * kernel control socket kcunit numbers start at 1 | |
269 | */ | |
270 | packet_manglers[sac->sc_unit - 1] = p_pkt_mnglr; | |
271 | ||
272 | p_pkt_mnglr->pkt_mnglr_kcref = kctlref; | |
273 | p_pkt_mnglr->pkt_mnglr_kcunit = sac->sc_unit; | |
274 | ||
275 | *unitinfo = p_pkt_mnglr; | |
276 | pkt_mnglr_active_count++; | |
277 | } | |
278 | ||
279 | p_pkt_mnglr->pkt_mnglr_ipfilter.cookie = p_pkt_mnglr; | |
280 | p_pkt_mnglr->pkt_mnglr_ipfilter.name = "com.apple.pktmnglripfilter"; | |
281 | p_pkt_mnglr->pkt_mnglr_ipfilter.ipf_input = pktmnglr_ipfilter_input; | |
282 | p_pkt_mnglr->pkt_mnglr_ipfilter.ipf_output = pktmnglr_ipfilter_output; | |
283 | p_pkt_mnglr->pkt_mnglr_ipfilter.ipf_detach = pktmnglr_ipfilter_detach; | |
284 | error = ipf_addv4(&(p_pkt_mnglr->pkt_mnglr_ipfilter), &(p_pkt_mnglr->pkt_mnglr_ipfref)); | |
285 | if (error) { | |
286 | PKT_MNGLR_LOG(LOG_ERR, "Could not register packet mangler's IPv4 Filter"); | |
287 | goto done; | |
288 | } | |
289 | error = ipf_addv6(&(p_pkt_mnglr->pkt_mnglr_ipfilter), &(p_pkt_mnglr->pkt_mnglr_ipfrefv6)); | |
290 | if (error) { | |
291 | ipf_remove(p_pkt_mnglr->pkt_mnglr_ipfref); | |
292 | PKT_MNGLR_LOG(LOG_ERR, "Could not register packet mangler's IPv6 Filter"); | |
293 | goto done; | |
294 | } | |
295 | ||
296 | PKT_MNGLR_LOG(LOG_INFO, "Registered packet mangler's IP Filters"); | |
0a7de745 | 297 | p_pkt_mnglr->pkt_mnglr_flags |= PKT_MNGLR_FLG_IPFILTER_ATTACHED; |
fe8ab488 A |
298 | pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw); |
299 | ||
300 | done: | |
0a7de745 | 301 | if (error != 0 && p_pkt_mnglr != NULL) { |
fe8ab488 | 302 | zfree(packet_mangler_zone, p_pkt_mnglr); |
0a7de745 | 303 | } |
fe8ab488 A |
304 | |
305 | PKT_MNGLR_LOG(LOG_INFO, "return %d pkt_mnglr_active_count %u kcunit %u", | |
306 | error, pkt_mnglr_active_count, sac->sc_unit); | |
307 | ||
0a7de745 | 308 | return error; |
fe8ab488 A |
309 | } |
310 | ||
311 | static errno_t | |
312 | pkt_mnglr_ctl_disconnect(kern_ctl_ref kctlref, u_int32_t kcunit, void *unitinfo) | |
313 | { | |
314 | #pragma unused(kctlref) | |
0a7de745 | 315 | errno_t error = 0; |
fe8ab488 A |
316 | struct packet_mangler *p_pkt_mnglr; |
317 | ||
318 | PKT_MNGLR_LOG(LOG_INFO, "Disconnecting packet mangler kernel control"); | |
319 | ||
320 | if (packet_manglers == NULL) { | |
321 | PKT_MNGLR_LOG(LOG_ERR, "no packet filter"); | |
322 | error = EINVAL; | |
323 | goto done; | |
324 | } | |
325 | if (kcunit > MAX_PACKET_MANGLER) { | |
326 | PKT_MNGLR_LOG(LOG_ERR, "kcunit %u > MAX_PACKET_MANGLER (%d)", | |
327 | kcunit, MAX_PACKET_MANGLER); | |
328 | error = EINVAL; | |
329 | goto done; | |
330 | } | |
331 | ||
332 | p_pkt_mnglr = (struct packet_mangler *)unitinfo; | |
333 | if (p_pkt_mnglr == NULL) { | |
334 | PKT_MNGLR_LOG(LOG_ERR, "Unit info is NULL"); | |
335 | goto done; | |
336 | } | |
337 | ||
338 | pkt_mnglr_rw_lock_exclusive(&pkt_mnglr_lck_rw); | |
339 | if (packet_manglers[kcunit - 1] != p_pkt_mnglr || p_pkt_mnglr->pkt_mnglr_kcunit != kcunit) { | |
340 | PKT_MNGLR_LOG(LOG_ERR, "bad unit info %u)", | |
341 | kcunit); | |
342 | pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw); | |
343 | goto done; | |
344 | } | |
345 | ||
346 | /* | |
347 | * Make filter inactive | |
348 | */ | |
349 | packet_manglers[kcunit - 1] = NULL; | |
350 | pkt_mnglr_active_count--; | |
351 | if (p_pkt_mnglr->pkt_mnglr_flags & PKT_MNGLR_FLG_IPFILTER_ATTACHED) { | |
352 | (void) ipf_remove(p_pkt_mnglr->pkt_mnglr_ipfref); | |
353 | (void) ipf_remove(p_pkt_mnglr->pkt_mnglr_ipfrefv6); | |
354 | } | |
355 | pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw); | |
356 | zfree(packet_mangler_zone, p_pkt_mnglr); | |
357 | done: | |
358 | PKT_MNGLR_LOG(LOG_INFO, "return %d pkt_mnglr_active_count %u kcunit %u", | |
359 | error, pkt_mnglr_active_count, kcunit); | |
360 | ||
0a7de745 | 361 | return error; |
fe8ab488 A |
362 | } |
363 | ||
364 | static errno_t | |
365 | pkt_mnglr_ctl_getopt(kern_ctl_ref kctlref, u_int32_t kcunit, void *unitinfo, | |
0a7de745 | 366 | int opt, void *data, size_t *len) |
fe8ab488 A |
367 | { |
368 | #pragma unused(kctlref, opt) | |
0a7de745 | 369 | errno_t error = 0; |
fe8ab488 A |
370 | struct packet_mangler *p_pkt_mnglr = (struct packet_mangler *)unitinfo; |
371 | ||
372 | PKT_MNGLR_LOG(LOG_NOTICE, ""); | |
373 | ||
374 | pkt_mnglr_rw_lock_shared(&pkt_mnglr_lck_rw); | |
375 | ||
376 | if (packet_manglers == NULL) { | |
377 | PKT_MNGLR_LOG(LOG_ERR, "no packet filter"); | |
378 | error = EINVAL; | |
379 | goto done; | |
380 | } | |
381 | if (kcunit > MAX_PACKET_MANGLER) { | |
382 | PKT_MNGLR_LOG(LOG_ERR, "kcunit %u > MAX_PACKET_MANGLER (%d)", | |
383 | kcunit, MAX_PACKET_MANGLER); | |
384 | error = EINVAL; | |
385 | goto done; | |
386 | } | |
387 | if (p_pkt_mnglr != (void *)packet_manglers[kcunit - 1]) { | |
388 | PKT_MNGLR_LOG(LOG_ERR, "unitinfo does not match for kcunit %u", | |
389 | kcunit); | |
390 | error = EINVAL; | |
391 | goto done; | |
392 | } | |
393 | switch (opt) { | |
0a7de745 A |
394 | case PKT_MNGLR_OPT_PROTO_ACT_MASK: |
395 | if (*len < sizeof(uint32_t)) { | |
396 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTO_ACT_MASK " | |
397 | "len too small %lu", *len); | |
398 | error = EINVAL; | |
399 | goto done; | |
400 | } | |
fe8ab488 | 401 | |
0a7de745 A |
402 | if (data != NULL) { |
403 | *(uint32_t *)data = p_pkt_mnglr->proto_action_mask; | |
404 | } | |
405 | break; | |
406 | case PKT_MNGLR_OPT_IP_ACT_MASK: | |
407 | if (*len < sizeof(uint32_t)) { | |
408 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_IP_ACT_MASK " | |
409 | "len too small %lu", *len); | |
410 | error = EINVAL; | |
411 | goto done; | |
412 | } | |
fe8ab488 | 413 | |
0a7de745 A |
414 | if (data != NULL) { |
415 | *(uint32_t *)data = p_pkt_mnglr->ip_action_mask; | |
416 | } | |
417 | break; | |
418 | case PKT_MNGLR_OPT_LOCAL_IP: | |
419 | if (*len < sizeof(struct sockaddr_storage)) { | |
420 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_IP " | |
421 | "len too small %lu", *len); | |
422 | error = EINVAL; | |
423 | goto done; | |
424 | } | |
fe8ab488 | 425 | |
0a7de745 A |
426 | if (data != NULL) { |
427 | *(struct sockaddr_storage *)data = p_pkt_mnglr->lsaddr; | |
428 | } | |
429 | break; | |
430 | case PKT_MNGLR_OPT_REMOTE_IP: | |
431 | if (*len < sizeof(struct sockaddr_storage)) { | |
432 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_IP " | |
433 | "len too small %lu", *len); | |
434 | error = EINVAL; | |
435 | goto done; | |
436 | } | |
fe8ab488 | 437 | |
0a7de745 A |
438 | if (data != NULL) { |
439 | *(struct sockaddr_storage *)data = p_pkt_mnglr->rsaddr; | |
440 | } | |
441 | break; | |
442 | case PKT_MNGLR_OPT_LOCAL_PORT: | |
443 | if (*len < sizeof(uint16_t)) { | |
444 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_PORT " | |
445 | "len too small %lu", *len); | |
446 | error = EINVAL; | |
447 | goto done; | |
448 | } | |
fe8ab488 | 449 | |
0a7de745 A |
450 | if (data != NULL) { |
451 | *(uint16_t *)data = p_pkt_mnglr->lport; | |
452 | } | |
453 | break; | |
454 | case PKT_MNGLR_OPT_REMOTE_PORT: | |
455 | if (*len < sizeof(uint16_t)) { | |
456 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_PORT " | |
457 | "len too small %lu", *len); | |
458 | error = EINVAL; | |
459 | goto done; | |
460 | } | |
fe8ab488 | 461 | |
0a7de745 A |
462 | if (data != NULL) { |
463 | *(uint16_t *)data = p_pkt_mnglr->rport; | |
464 | } | |
465 | break; | |
466 | case PKT_MNGLR_OPT_DIRECTION: | |
467 | if (*len < sizeof(uint32_t)) { | |
468 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_DIRECTION " | |
469 | "len too small %lu", *len); | |
470 | error = EINVAL; | |
471 | goto done; | |
472 | } | |
473 | if (data != NULL) { | |
474 | *(uint32_t *)data = p_pkt_mnglr->dir; | |
475 | } | |
476 | break; | |
477 | case PKT_MNGLR_OPT_PROTOCOL: | |
478 | if (*len < sizeof(uint32_t)) { | |
479 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTOCOL " | |
480 | "len too small %lu", *len); | |
481 | error = EINVAL; | |
482 | goto done; | |
483 | } | |
484 | if (data != NULL) { | |
485 | *(uint32_t *)data = p_pkt_mnglr->proto; | |
486 | } | |
487 | break; | |
488 | case PKT_MNGLR_OPT_ACTIVATE: | |
489 | if (*len < sizeof(uint8_t)) { | |
490 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_ACTIVATE " | |
491 | "len too small %lu", *len); | |
492 | error = EINVAL; | |
493 | goto done; | |
494 | } | |
fe8ab488 | 495 | |
0a7de745 A |
496 | if (data != NULL) { |
497 | *(uint8_t *)data = p_pkt_mnglr->activate; | |
498 | } | |
499 | break; | |
500 | default: | |
501 | error = ENOPROTOOPT; | |
502 | break; | |
fe8ab488 A |
503 | } |
504 | done: | |
505 | pkt_mnglr_rw_unlock_shared(&pkt_mnglr_lck_rw); | |
506 | ||
0a7de745 | 507 | return error; |
fe8ab488 A |
508 | } |
509 | ||
510 | static errno_t | |
511 | pkt_mnglr_ctl_setopt(kern_ctl_ref kctlref, u_int32_t kcunit, void *unitinfo, | |
0a7de745 | 512 | int opt, void *data, size_t len) |
fe8ab488 A |
513 | { |
514 | #pragma unused(kctlref, opt) | |
0a7de745 | 515 | errno_t error = 0; |
fe8ab488 A |
516 | struct packet_mangler *p_pkt_mnglr = (struct packet_mangler *)unitinfo; |
517 | ||
518 | PKT_MNGLR_LOG(LOG_NOTICE, ""); | |
519 | ||
520 | pkt_mnglr_rw_lock_exclusive(&pkt_mnglr_lck_rw); | |
521 | ||
522 | if (packet_manglers == NULL) { | |
523 | PKT_MNGLR_LOG(LOG_ERR, "no packet filter"); | |
524 | error = EINVAL; | |
525 | goto done; | |
526 | } | |
527 | if (kcunit > MAX_PACKET_MANGLER) { | |
528 | PKT_MNGLR_LOG(LOG_ERR, "kcunit %u > MAX_PACKET_MANGLER (%d)", | |
529 | kcunit, MAX_PACKET_MANGLER); | |
530 | error = EINVAL; | |
531 | goto done; | |
532 | } | |
533 | if (p_pkt_mnglr != (void *)packet_manglers[kcunit - 1]) { | |
534 | PKT_MNGLR_LOG(LOG_ERR, "unitinfo does not match for kcunit %u", | |
535 | kcunit); | |
536 | error = EINVAL; | |
537 | goto done; | |
538 | } | |
539 | switch (opt) { | |
0a7de745 A |
540 | case PKT_MNGLR_OPT_PROTO_ACT_MASK: |
541 | if (len < sizeof(uint32_t)) { | |
542 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTO_ACT_MASK " | |
543 | "len too small %lu", len); | |
544 | error = EINVAL; | |
545 | goto done; | |
546 | } | |
547 | if (p_pkt_mnglr->proto_action_mask != 0) { | |
548 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTO_ACT_MASK " | |
549 | "already set %u", | |
550 | p_pkt_mnglr->proto_action_mask); | |
551 | error = EINVAL; | |
552 | goto done; | |
553 | } | |
554 | p_pkt_mnglr->proto_action_mask = *(uint32_t *)data; | |
555 | PKT_MNGLR_LOG(LOG_INFO, "p_pkt_mnglr->proto_action_mask set to :%d", p_pkt_mnglr->proto_action_mask); | |
556 | break; | |
557 | case PKT_MNGLR_OPT_IP_ACT_MASK: | |
558 | if (len < sizeof(uint32_t)) { | |
559 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_IP_ACT_MASK " | |
560 | "len too small %lu", len); | |
561 | error = EINVAL; | |
562 | goto done; | |
563 | } | |
564 | if (p_pkt_mnglr->ip_action_mask != 0) { | |
565 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_IP_ACT_MASK " | |
566 | "already set %u", | |
567 | p_pkt_mnglr->ip_action_mask); | |
568 | error = EINVAL; | |
569 | goto done; | |
570 | } | |
571 | p_pkt_mnglr->ip_action_mask = *(uint32_t *)data; | |
572 | break; | |
573 | case PKT_MNGLR_OPT_LOCAL_IP: | |
574 | if (len < sizeof(struct sockaddr_storage)) { | |
575 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_IP " | |
576 | "len too small %lu", len); | |
577 | error = EINVAL; | |
578 | goto done; | |
579 | } | |
580 | if (p_pkt_mnglr->lsaddr.ss_family) { | |
581 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_IP " | |
582 | "already set"); | |
583 | error = EINVAL; | |
584 | goto done; | |
585 | } | |
586 | p_pkt_mnglr->lsaddr = *(struct sockaddr_storage *)data; | |
587 | break; | |
588 | case PKT_MNGLR_OPT_REMOTE_IP: | |
589 | if (len < sizeof(struct sockaddr_storage)) { | |
590 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_IP " | |
591 | "len too small %lu", len); | |
592 | error = EINVAL; | |
593 | goto done; | |
594 | } | |
595 | if (p_pkt_mnglr->rsaddr.ss_family) { | |
596 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_IP " | |
597 | "already set"); | |
598 | error = EINVAL; | |
599 | goto done; | |
600 | } | |
fe8ab488 | 601 | |
0a7de745 A |
602 | p_pkt_mnglr->rsaddr = *(struct sockaddr_storage *)data; |
603 | PKT_MNGLR_LOG(LOG_INFO, | |
604 | "Remote IP registered for address family: %d", | |
605 | p_pkt_mnglr->rsaddr.ss_family); | |
606 | break; | |
607 | case PKT_MNGLR_OPT_LOCAL_PORT: | |
608 | if (len < sizeof(uint16_t)) { | |
609 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_PORT " | |
610 | "len too small %lu", len); | |
611 | error = EINVAL; | |
612 | goto done; | |
613 | } | |
614 | if (p_pkt_mnglr->lport != 0) { | |
615 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_PORT " | |
616 | "already set %d", | |
617 | p_pkt_mnglr->lport); | |
618 | error = EINVAL; | |
619 | goto done; | |
620 | } | |
621 | p_pkt_mnglr->lport = *(uint16_t *)data; | |
622 | break; | |
623 | case PKT_MNGLR_OPT_REMOTE_PORT: | |
624 | if (len < sizeof(uint16_t)) { | |
625 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_PORT " | |
626 | "len too small %lu", len); | |
627 | error = EINVAL; | |
628 | goto done; | |
629 | } | |
630 | if (p_pkt_mnglr->rport != 0) { | |
631 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_PORT " | |
632 | "already set %d", | |
633 | p_pkt_mnglr->rport); | |
634 | error = EINVAL; | |
635 | goto done; | |
636 | } | |
637 | p_pkt_mnglr->rport = *(uint16_t *)data; | |
638 | break; | |
639 | case PKT_MNGLR_OPT_DIRECTION: | |
640 | if (len < sizeof(uint32_t)) { | |
641 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_DIRECTION " | |
642 | "len too small %lu", len); | |
643 | error = EINVAL; | |
644 | goto done; | |
645 | } | |
646 | if (p_pkt_mnglr->dir != 0) { | |
647 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_DIRECTION " | |
648 | "already set %u", | |
649 | p_pkt_mnglr->dir); | |
650 | error = EINVAL; | |
651 | goto done; | |
652 | } | |
653 | p_pkt_mnglr->dir = *(uint32_t *)data; | |
654 | break; | |
655 | case PKT_MNGLR_OPT_PROTOCOL: | |
656 | if (len < sizeof(uint32_t)) { | |
657 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTOCOL " | |
658 | "len too small %lu", len); | |
659 | error = EINVAL; | |
660 | goto done; | |
661 | } | |
662 | if (p_pkt_mnglr->proto != 0) { | |
663 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTOCOL " | |
664 | "already set %u", | |
665 | p_pkt_mnglr->proto); | |
666 | error = EINVAL; | |
667 | goto done; | |
668 | } | |
669 | p_pkt_mnglr->proto = *(uint32_t *)data; | |
670 | break; | |
671 | case PKT_MNGLR_OPT_ACTIVATE: | |
672 | if (len < sizeof(uint8_t)) { | |
673 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_ACTIVATE " | |
674 | "len too small %lu", len); | |
675 | error = EINVAL; | |
676 | goto done; | |
677 | } | |
678 | if (p_pkt_mnglr->activate != 0) { | |
679 | PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_ACTIVATE " | |
680 | "already set %u", | |
fe8ab488 | 681 | p_pkt_mnglr->activate); |
0a7de745 A |
682 | error = EINVAL; |
683 | goto done; | |
684 | } | |
685 | p_pkt_mnglr->activate = *(uint8_t *)data; | |
686 | PKT_MNGLR_LOG(LOG_ERR, "p_pkt_mnglr->activate set to :%d", | |
687 | p_pkt_mnglr->activate); | |
688 | break; | |
689 | default: | |
690 | error = ENOPROTOOPT; | |
691 | break; | |
fe8ab488 A |
692 | } |
693 | done: | |
694 | pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw); | |
695 | ||
0a7de745 | 696 | return error; |
fe8ab488 A |
697 | } |
698 | ||
699 | void | |
700 | pkt_mnglr_init(void) | |
701 | { | |
702 | struct kern_ctl_reg kern_ctl; | |
0a7de745 | 703 | errno_t error = 0; |
fe8ab488 A |
704 | vm_size_t pkt_mnglr_size = 0; |
705 | ||
706 | PKT_MNGLR_LOG(LOG_NOTICE, ""); | |
707 | ||
708 | /* | |
709 | * Compile time verifications | |
710 | */ | |
711 | _CASSERT(PKT_MNGLR_MAX_FILTER_COUNT == MAX_PACKET_MANGLER); | |
712 | ||
713 | /* | |
714 | * Zone for packet mangler kernel control sockets | |
715 | */ | |
716 | pkt_mnglr_size = sizeof(struct packet_mangler); | |
717 | packet_mangler_zone = zinit(pkt_mnglr_size, | |
718 | PACKET_MANGLER_ZONE_MAX * pkt_mnglr_size, | |
719 | 0, | |
720 | PACKET_MANGLER_ZONE_NAME); | |
721 | ||
722 | if (packet_mangler_zone == NULL) { | |
723 | panic("%s: zinit(%s) failed", __func__, | |
724 | PACKET_MANGLER_ZONE_NAME); | |
725 | /* NOTREACHED */ | |
726 | } | |
727 | zone_change(packet_mangler_zone, Z_CALLERACCT, FALSE); | |
728 | zone_change(packet_mangler_zone, Z_EXPAND, TRUE); | |
729 | ||
730 | /* | |
731 | * Allocate locks | |
732 | */ | |
733 | pkt_mnglr_lck_grp_attr = lck_grp_attr_alloc_init(); | |
734 | if (pkt_mnglr_lck_grp_attr == NULL) { | |
735 | panic("%s: lck_grp_attr_alloc_init failed", __func__); | |
736 | /* NOTREACHED */ | |
737 | } | |
738 | pkt_mnglr_lck_grp = lck_grp_alloc_init("packet manglerr", | |
739 | pkt_mnglr_lck_grp_attr); | |
740 | if (pkt_mnglr_lck_grp == NULL) { | |
741 | panic("%s: lck_grp_alloc_init failed", __func__); | |
742 | /* NOTREACHED */ | |
743 | } | |
744 | pkt_mnglr_lck_attr = lck_attr_alloc_init(); | |
745 | if (pkt_mnglr_lck_attr == NULL) { | |
746 | panic("%s: lck_attr_alloc_init failed", __func__); | |
747 | /* NOTREACHED */ | |
748 | } | |
749 | lck_rw_init(&pkt_mnglr_lck_rw, pkt_mnglr_lck_grp, pkt_mnglr_lck_attr); | |
750 | ||
751 | /* | |
752 | * Register kernel control | |
753 | */ | |
754 | bzero(&kern_ctl, sizeof(kern_ctl)); | |
755 | strlcpy(kern_ctl.ctl_name, PACKET_MANGLER_CONTROL_NAME, | |
756 | sizeof(kern_ctl.ctl_name)); | |
757 | kern_ctl.ctl_flags = CTL_FLAG_PRIVILEGED | CTL_FLAG_REG_EXTENDED; | |
758 | kern_ctl.ctl_connect = pkt_mnglr_ctl_connect; | |
759 | kern_ctl.ctl_disconnect = pkt_mnglr_ctl_disconnect; | |
760 | kern_ctl.ctl_getopt = pkt_mnglr_ctl_getopt; | |
761 | kern_ctl.ctl_setopt = pkt_mnglr_ctl_setopt; | |
762 | error = ctl_register(&kern_ctl, &pkt_mnglr_kctlref); | |
763 | if (error != 0) { | |
764 | PKT_MNGLR_LOG(LOG_ERR, "ctl_register failed: %d", error); | |
765 | } else { | |
766 | PKT_MNGLR_LOG(LOG_INFO, "Registered packet mangler kernel control."); | |
767 | } | |
768 | } | |
769 | ||
0a7de745 A |
770 | static errno_t |
771 | pktmnglr_ipfilter_output(void *cookie, mbuf_t *data, ipf_pktopts_t options) | |
fe8ab488 A |
772 | { |
773 | struct packet_mangler *p_pkt_mnglr = (struct packet_mangler *)cookie; | |
3e170ce0 A |
774 | struct ip ip; |
775 | struct tcphdr tcp; | |
fe8ab488 | 776 | int optlen = 0; |
3e170ce0 | 777 | errno_t error = 0; |
fe8ab488 A |
778 | |
779 | #pragma unused(tcp, optlen, options) | |
fe8ab488 | 780 | if (p_pkt_mnglr == NULL) { |
3e170ce0 | 781 | goto output_done; |
fe8ab488 A |
782 | } |
783 | ||
784 | if (!p_pkt_mnglr->activate) { | |
3e170ce0 A |
785 | goto output_done; |
786 | } | |
787 | ||
788 | if (p_pkt_mnglr->dir == IN) { | |
789 | goto output_done; | |
fe8ab488 A |
790 | } |
791 | ||
792 | if (data == NULL) { | |
3e170ce0 A |
793 | PKT_MNGLR_LOG(LOG_ERR, "Data pointer is NULL"); |
794 | goto output_done; | |
fe8ab488 A |
795 | } |
796 | ||
3e170ce0 A |
797 | /* Check for IP filter options */ |
798 | error = mbuf_copydata(*data, 0, sizeof(ip), &ip); | |
799 | if (error) { | |
800 | PKT_MNGLR_LOG(LOG_ERR, "Could not make local IP header copy"); | |
801 | goto output_done; | |
fe8ab488 A |
802 | } |
803 | ||
3e170ce0 A |
804 | if ((p_pkt_mnglr->lsaddr.ss_family == AF_INET6) && (ip.ip_v == 4)) { |
805 | goto output_done; | |
fe8ab488 A |
806 | } |
807 | ||
3e170ce0 A |
808 | if ((p_pkt_mnglr->lsaddr.ss_family == AF_INET) && (ip.ip_v == 6)) { |
809 | goto output_done; | |
fe8ab488 A |
810 | } |
811 | ||
812 | if (p_pkt_mnglr->lsaddr.ss_family == AF_INET) { | |
813 | struct sockaddr_in laddr = *(struct sockaddr_in *)(&(p_pkt_mnglr->lsaddr)); | |
3e170ce0 A |
814 | if (ip.ip_src.s_addr != laddr.sin_addr.s_addr) { |
815 | goto output_done; | |
fe8ab488 A |
816 | } |
817 | } | |
818 | ||
819 | if (p_pkt_mnglr->rsaddr.ss_family == AF_INET) { | |
820 | struct sockaddr_in raddr = *(struct sockaddr_in *)(&(p_pkt_mnglr->rsaddr)); | |
3e170ce0 A |
821 | if (ip.ip_dst.s_addr != raddr.sin_addr.s_addr) { |
822 | goto output_done; | |
fe8ab488 A |
823 | } |
824 | } | |
825 | ||
3e170ce0 A |
826 | if (ip.ip_v != 4) { |
827 | PKT_MNGLR_LOG(LOG_INFO, | |
828 | "%s:%d Not handling IP version %d\n", | |
5ba3f43e | 829 | __func__, __LINE__, ip.ip_v); |
3e170ce0 | 830 | goto output_done; |
fe8ab488 A |
831 | } |
832 | ||
3e170ce0 | 833 | output_done: |
fe8ab488 A |
834 | /* Not handling output flow */ |
835 | return 0; | |
836 | } | |
837 | ||
0a7de745 | 838 | #define TCP_MAX_OPTLEN 40 |
fe8ab488 | 839 | |
0a7de745 A |
840 | static errno_t |
841 | pktmnglr_ipfilter_input(void *cookie, mbuf_t *data, int offset, u_int8_t protocol) | |
fe8ab488 A |
842 | { |
843 | struct packet_mangler *p_pkt_mnglr = (struct packet_mangler *)cookie; | |
cb323159 | 844 | struct ip6_hdr ip6; |
fe8ab488 A |
845 | struct ip ip; |
846 | struct tcphdr tcp; | |
9d749ea3 | 847 | int ip_pld_len; |
fe8ab488 A |
848 | errno_t error = 0; |
849 | ||
850 | if (p_pkt_mnglr == NULL) { | |
851 | PKT_MNGLR_LOG(LOG_ERR, "p_pkt_mnglr is NULL"); | |
852 | goto input_done; | |
853 | } | |
854 | ||
855 | if (p_pkt_mnglr->activate == 0) { | |
856 | PKT_MNGLR_LOG(LOG_INFO, "p_pkt_mnglr not yet activated"); | |
857 | goto input_done; | |
858 | } | |
859 | ||
3e170ce0 | 860 | if (p_pkt_mnglr->dir == OUT) { |
fe8ab488 A |
861 | goto input_done; |
862 | } | |
863 | ||
3e170ce0 A |
864 | if (data == NULL) { |
865 | PKT_MNGLR_LOG(LOG_ERR, "Data pointer is NULL"); | |
fe8ab488 A |
866 | goto input_done; |
867 | } | |
868 | ||
869 | /* Check for IP filter options */ | |
870 | error = mbuf_copydata(*data, 0, sizeof(ip), &ip); | |
871 | if (error) { | |
872 | PKT_MNGLR_LOG(LOG_ERR, "Could not make local IP header copy"); | |
873 | goto input_done; | |
874 | } | |
875 | ||
cb323159 A |
876 | if (ip.ip_v == 6) { |
877 | error = mbuf_copydata(*data, 0, sizeof(ip6), &ip6); | |
878 | if (error) { | |
879 | PKT_MNGLR_LOG(LOG_ERR, "Could not make local IPv6 header copy"); | |
880 | goto input_done; | |
881 | } | |
882 | } | |
883 | ||
fe8ab488 A |
884 | if ((p_pkt_mnglr->lsaddr.ss_family == AF_INET6) && (ip.ip_v == 4)) { |
885 | PKT_MNGLR_LOG(LOG_INFO, "Skipping filtering as address family of packet is IPv4 but local " | |
886 | "address is set to IPv6"); | |
887 | goto input_done; | |
888 | } | |
889 | ||
890 | if ((p_pkt_mnglr->lsaddr.ss_family == AF_INET) && (ip.ip_v == 6)) { | |
891 | PKT_MNGLR_LOG(LOG_INFO, "Skipping filtering as address family " | |
892 | "of packet is IPv6 but local address is set to IPv4"); | |
893 | goto input_done; | |
894 | } | |
895 | ||
896 | if (p_pkt_mnglr->lsaddr.ss_family == AF_INET) { | |
897 | struct sockaddr_in laddr = *(struct sockaddr_in *)(&(p_pkt_mnglr->lsaddr)); | |
898 | if (ip.ip_dst.s_addr != laddr.sin_addr.s_addr) { | |
899 | goto input_done; | |
900 | } | |
cb323159 A |
901 | } else if (p_pkt_mnglr->lsaddr.ss_family == AF_INET6) { |
902 | struct sockaddr_in6 laddr = *(struct sockaddr_in6 *)(&(p_pkt_mnglr->lsaddr)); | |
903 | if (!IN6_ARE_ADDR_EQUAL(&ip6.ip6_dst, &laddr.sin6_addr)) { | |
904 | goto input_done; | |
905 | } | |
fe8ab488 A |
906 | } |
907 | ||
908 | if (p_pkt_mnglr->rsaddr.ss_family == AF_INET) { | |
909 | struct sockaddr_in raddr = *(struct sockaddr_in *)(&(p_pkt_mnglr->rsaddr)); | |
910 | if (ip.ip_src.s_addr != raddr.sin_addr.s_addr) { | |
911 | goto input_done; | |
912 | } | |
913 | PKT_MNGLR_LOG(LOG_INFO, "Remote IP: %x Source IP: %x in input path", | |
914 | raddr.sin_addr.s_addr, | |
915 | ip.ip_src.s_addr); | |
cb323159 A |
916 | } else if (p_pkt_mnglr->rsaddr.ss_family == AF_INET6) { |
917 | struct sockaddr_in6 raddr = *(struct sockaddr_in6 *)(&(p_pkt_mnglr->rsaddr)); | |
918 | if (!IN6_ARE_ADDR_EQUAL(&ip6.ip6_src, &raddr.sin6_addr)) { | |
919 | goto input_done; | |
920 | } | |
fe8ab488 A |
921 | } |
922 | ||
cb323159 A |
923 | if (ip.ip_v == 4) { |
924 | ip_pld_len = ntohs(ip.ip_len) - (ip.ip_hl << 2); | |
925 | } else if (ip.ip_v == 6) { | |
926 | if (ip6.ip6_nxt != p_pkt_mnglr->proto) { | |
927 | /* Don't support IPv6 extension headers */ | |
928 | goto input_done; | |
929 | } | |
930 | ip_pld_len = ntohs(ip6.ip6_plen) + sizeof(struct ip6_hdr); | |
931 | } else { | |
fe8ab488 A |
932 | goto input_done; |
933 | } | |
934 | ||
9d749ea3 | 935 | |
fe8ab488 A |
936 | if (protocol != p_pkt_mnglr->proto) { |
937 | PKT_MNGLR_LOG(LOG_INFO, "Skip: Protocol mismatch"); | |
938 | goto input_done; | |
939 | } | |
940 | ||
941 | switch (protocol) { | |
0a7de745 A |
942 | case IPPROTO_TCP: |
943 | if (ip_pld_len < (int) sizeof(tcp)) { | |
944 | PKT_MNGLR_LOG(LOG_ERR, "IP total len not big enough for TCP: %d", ip_pld_len); | |
945 | goto drop_it; | |
946 | } | |
fe8ab488 | 947 | |
0a7de745 A |
948 | error = mbuf_copydata(*data, offset, sizeof(tcp), &tcp); |
949 | if (error) { | |
950 | PKT_MNGLR_LOG(LOG_ERR, "Could not make local TCP header copy"); | |
fe8ab488 | 951 | goto input_done; |
0a7de745 A |
952 | } |
953 | ||
954 | if (p_pkt_mnglr->lport && (p_pkt_mnglr->lport != tcp.th_dport)) { | |
955 | PKT_MNGLR_LOG(LOG_INFO, "Local port and IP des port do not match"); | |
fe8ab488 | 956 | goto input_done; |
0a7de745 A |
957 | } |
958 | ||
959 | if (p_pkt_mnglr->rport && (p_pkt_mnglr->rport != tcp.th_sport)) { | |
960 | PKT_MNGLR_LOG(LOG_INFO, "Remote port and IP src port do not match"); | |
fe8ab488 | 961 | goto input_done; |
0a7de745 A |
962 | } |
963 | break; | |
964 | case IPPROTO_UDP: | |
965 | goto input_done; | |
966 | case IPPROTO_ICMP: | |
967 | goto input_done; | |
968 | case IPPROTO_ICMPV6: | |
969 | goto input_done; | |
970 | default: | |
971 | goto input_done; | |
fe8ab488 A |
972 | } |
973 | ||
974 | /* XXX Do IP actions here */ | |
975 | PKT_MNGLR_LOG(LOG_INFO, "Proceeding with packet mangler actions on the packet"); | |
976 | ||
977 | /* Protocol actions */ | |
978 | switch (protocol) { | |
0a7de745 A |
979 | case IPPROTO_TCP: |
980 | if (p_pkt_mnglr->proto_action_mask) { | |
981 | char tcp_opt_buf[TCP_MAX_OPTLEN] = {0}; | |
982 | int orig_tcp_optlen; | |
983 | int tcp_optlen = 0; | |
984 | int i = 0, off; | |
9d749ea3 | 985 | |
0a7de745 | 986 | off = (tcp.th_off << 2); |
9d749ea3 | 987 | |
0a7de745 A |
988 | if (off < (int) sizeof(struct tcphdr) || off > ip_pld_len) { |
989 | PKT_MNGLR_LOG(LOG_ERR, "TCP header offset is wrong: %d", off); | |
990 | goto drop_it; | |
991 | } | |
9d749ea3 | 992 | |
0a7de745 A |
993 | |
994 | tcp_optlen = off - sizeof(struct tcphdr); | |
995 | ||
996 | PKT_MNGLR_LOG(LOG_INFO, "Packet from F5 is TCP\n"); | |
997 | PKT_MNGLR_LOG(LOG_INFO, "Optlen: %d\n", tcp_optlen); | |
998 | orig_tcp_optlen = tcp_optlen; | |
999 | if (orig_tcp_optlen) { | |
1000 | error = mbuf_copydata(*data, offset + sizeof(struct tcphdr), orig_tcp_optlen, tcp_opt_buf); | |
1001 | if (error) { | |
1002 | PKT_MNGLR_LOG(LOG_ERR, "Failed to copy tcp options: error %d offset %d optlen %d", error, offset, orig_tcp_optlen); | |
1003 | goto input_done; | |
fe8ab488 | 1004 | } |
0a7de745 | 1005 | } |
fe8ab488 | 1006 | |
0a7de745 A |
1007 | while (tcp_optlen > 0) { |
1008 | if (tcp_opt_buf[i] == 0x1) { | |
1009 | PKT_MNGLR_LOG(LOG_INFO, "Skipping NOP\n"); | |
1010 | tcp_optlen--; | |
1011 | i++; | |
1012 | continue; | |
1013 | } else if ((tcp_opt_buf[i] != 0) && (tcp_opt_buf[i] != TCP_OPT_MULTIPATH_TCP)) { | |
1014 | PKT_MNGLR_LOG(LOG_INFO, "Skipping option %x\n", tcp_opt_buf[i]); | |
1015 | ||
1016 | /* Minimum TCP option size is 2 */ | |
1017 | if (tcp_opt_buf[i + 1] < 2) { | |
1018 | PKT_MNGLR_LOG(LOG_ERR, "Received suspicious TCP option"); | |
1019 | goto drop_it; | |
1020 | } | |
1021 | tcp_optlen -= tcp_opt_buf[i + 1]; | |
1022 | i += tcp_opt_buf[i + 1]; | |
1023 | continue; | |
1024 | } else if (tcp_opt_buf[i] == TCP_OPT_MULTIPATH_TCP) { | |
1025 | int j = 0; | |
1026 | unsigned char mptcpoptlen = tcp_opt_buf[i + 1]; | |
1027 | uint8_t sbtver = tcp_opt_buf[i + MPTCP_SBT_VER_OFFSET]; | |
1028 | uint8_t subtype = sbtver >> 4; | |
1029 | ||
1030 | PKT_MNGLR_LOG(LOG_INFO, "Got MPTCP option %x\n", tcp_opt_buf[i]); | |
1031 | PKT_MNGLR_LOG(LOG_INFO, "Got MPTCP subtype %x\n", subtype); | |
1032 | if (subtype == MPTCP_SUBTYPE_DSS) { | |
1033 | PKT_MNGLR_LOG(LOG_INFO, "Got DSS option\n"); | |
1034 | PKT_MNGLR_LOG(LOG_INFO, "Protocol option mask: %d\n", p_pkt_mnglr->proto_action_mask); | |
1035 | if (p_pkt_mnglr->proto_action_mask & | |
1036 | PKT_MNGLR_TCP_ACT_DSS_DROP) { | |
9d749ea3 A |
1037 | goto drop_it; |
1038 | } | |
0a7de745 | 1039 | } |
3e170ce0 | 1040 | |
0a7de745 A |
1041 | PKT_MNGLR_LOG(LOG_INFO, "Got MPTCP option %x\n", tcp_opt_buf[i]); |
1042 | for (; j < mptcpoptlen && j < tcp_optlen; j++) { | |
1043 | if (p_pkt_mnglr->proto_action_mask & | |
1044 | PKT_MNGLR_TCP_ACT_NOP_MPTCP) { | |
1045 | tcp_opt_buf[i + j] = 0x1; | |
fe8ab488 | 1046 | } |
fe8ab488 | 1047 | } |
0a7de745 A |
1048 | tcp_optlen -= mptcpoptlen; |
1049 | i += mptcpoptlen; | |
1050 | } else { | |
1051 | tcp_optlen--; | |
1052 | i++; | |
fe8ab488 | 1053 | } |
0a7de745 | 1054 | } |
9d749ea3 | 1055 | |
0a7de745 A |
1056 | if (orig_tcp_optlen) { |
1057 | error = mbuf_copyback(*data, | |
1058 | offset + sizeof(struct tcphdr), | |
1059 | orig_tcp_optlen, tcp_opt_buf, MBUF_WAITOK); | |
9d749ea3 | 1060 | |
0a7de745 A |
1061 | if (error) { |
1062 | PKT_MNGLR_LOG(LOG_ERR, | |
1063 | "Failed to copy tcp options back: error %d offset %d optlen %d", | |
1064 | error, offset, orig_tcp_optlen); | |
1065 | goto input_done; | |
fe8ab488 A |
1066 | } |
1067 | } | |
0a7de745 A |
1068 | } |
1069 | break; | |
1070 | case IPPROTO_UDP: | |
1071 | /* Don't handle UDP */ | |
1072 | break; | |
1073 | case IPPROTO_ICMP: | |
1074 | break; | |
1075 | case IPPROTO_ICMPV6: | |
1076 | break; | |
1077 | default: | |
1078 | break; | |
fe8ab488 A |
1079 | } |
1080 | chksm_update(*data); | |
1081 | input_done: | |
1082 | return 0; | |
3e170ce0 A |
1083 | |
1084 | drop_it: | |
1085 | PKT_MNGLR_LOG(LOG_INFO, "Dropping packet\n"); | |
1086 | mbuf_freem(*data); | |
1087 | return EJUSTRETURN; | |
fe8ab488 A |
1088 | } |
1089 | ||
0a7de745 A |
1090 | static void |
1091 | pktmnglr_ipfilter_detach(void *cookie) | |
fe8ab488 A |
1092 | { |
1093 | #pragma unused(cookie) | |
1094 | return; | |
1095 | } | |
1096 | ||
1097 | /* XXX Still need to modify this to use mbuf_copy* macros */ | |
0a7de745 A |
1098 | static void |
1099 | chksm_update(mbuf_t data) | |
fe8ab488 A |
1100 | { |
1101 | u_int16_t ip_sum; | |
1102 | u_int16_t tsum; | |
1103 | struct tcphdr *tcp; | |
d9a64523 | 1104 | errno_t err; |
fe8ab488 A |
1105 | |
1106 | unsigned char *ptr = (unsigned char *)mbuf_data(data); | |
1107 | struct ip *ip = (struct ip *)(void *)ptr; | |
1108 | if (ip->ip_v != 4) { | |
1109 | return; | |
1110 | } | |
1111 | ||
1112 | ip->ip_sum = 0; | |
d9a64523 | 1113 | err = mbuf_inet_cksum(data, 0, 0, ip->ip_hl << 2, &ip_sum); // ip sum |
0a7de745 | 1114 | if (err == 0) { |
d9a64523 | 1115 | ip->ip_sum = ip_sum; |
0a7de745 | 1116 | } |
fe8ab488 | 1117 | switch (ip->ip_p) { |
0a7de745 A |
1118 | case IPPROTO_TCP: |
1119 | tcp = (struct tcphdr *)(void *)(ptr + (ip->ip_hl << 2)); | |
1120 | tcp->th_sum = 0; | |
1121 | err = mbuf_inet_cksum(data, IPPROTO_TCP, ip->ip_hl << 2, | |
1122 | ntohs(ip->ip_len) - (ip->ip_hl << 2), &tsum); | |
1123 | if (err == 0) { | |
1124 | tcp->th_sum = tsum; | |
1125 | } | |
1126 | break; | |
1127 | case IPPROTO_UDP: | |
1128 | /* Don't handle UDP */ | |
1129 | break; | |
1130 | case IPPROTO_ICMP: | |
1131 | break; | |
1132 | case IPPROTO_ICMPV6: | |
1133 | break; | |
1134 | default: | |
1135 | break; | |
fe8ab488 A |
1136 | } |
1137 | ||
1138 | mbuf_clear_csum_performed(data); | |
1139 | return; | |
1140 | } |