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