1/*
2 * Copyright (c) 2004-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/param.h>	/* for definition of NULL */
30#include <sys/errno.h>
31#include <sys/malloc.h>
32#include <sys/socket.h>
33#include <sys/mbuf.h>
34#include <sys/systm.h>
35#include <libkern/OSAtomic.h>
36
37#include <machine/endian.h>
38
39#define _IP_VHL
40#include <net/if_var.h>
41#include <net/route.h>
42#include <net/kpi_protocol.h>
43
44#include <netinet/in_systm.h>
45#include <netinet/in.h>
46#include <netinet/in_var.h>
47#include <netinet6/in6_var.h>
48#include <netinet/ip.h>
49#include <netinet/ip6.h>
50#include <netinet/ip_var.h>
51#include <netinet6/ip6_var.h>
52#include <netinet/kpi_ipfilter_var.h>
53
54
55/*
56 * kipf_lock and kipf_ref protect the linkage of the list of IP filters
57 * An IP filter can be removed only when kipf_ref is zero
58 * If an IP filter cannot be removed because kipf_ref is not null, then
59 * the IP filter is marjed and kipf_delayed_remove is set so that when
60 * kipf_ref eventually goes down to zero, the IP filter is removed
61 */
62decl_lck_mtx_data(static, kipf_lock_data);
63static lck_mtx_t *kipf_lock = &kipf_lock_data;
64static u_int32_t kipf_ref = 0;
65static u_int32_t kipf_delayed_remove = 0;
66u_int32_t kipf_count = 0;
67
68__private_extern__ struct ipfilter_list	ipv4_filters = TAILQ_HEAD_INITIALIZER(ipv4_filters);
69__private_extern__ struct ipfilter_list	ipv6_filters = TAILQ_HEAD_INITIALIZER(ipv6_filters);
70__private_extern__ struct ipfilter_list	tbr_filters = TAILQ_HEAD_INITIALIZER(tbr_filters);
71
72__private_extern__ void
73ipf_ref(void)
74{
75	lck_mtx_lock(kipf_lock);
76    kipf_ref++;
77	lck_mtx_unlock(kipf_lock);
78}
79
80__private_extern__ void
81ipf_unref(void)
82{
83	lck_mtx_lock(kipf_lock);
84
85    if (kipf_ref == 0)
86    	panic("ipf_unref: kipf_ref == 0\n");
87
88    kipf_ref--;
89    if (kipf_ref == 0 && kipf_delayed_remove != 0) {
90    	struct ipfilter *filter;
91
92		while ((filter = TAILQ_FIRST(&tbr_filters))) {
93			ipf_detach_func ipf_detach = filter->ipf_filter.ipf_detach;
94			void* cookie = filter->ipf_filter.cookie;
95
96			TAILQ_REMOVE(filter->ipf_head, filter, ipf_link);
97			TAILQ_REMOVE(&tbr_filters, filter, ipf_tbr);
98			kipf_delayed_remove--;
99
100			if (ipf_detach) {
101				lck_mtx_unlock(kipf_lock);
102				ipf_detach(cookie);
103				lck_mtx_lock(kipf_lock);
104				/* In case some filter got to run while we released the lock */
105				if (kipf_ref != 0)
106					break;
107			}
108		}
109   	}
110	lck_mtx_unlock(kipf_lock);
111}
112
113static errno_t
114ipf_add(
115	const struct ipf_filter* filter,
116	ipfilter_t *filter_ref,
117	struct ipfilter_list *head)
118{
119	struct ipfilter	*new_filter;
120	if (filter->name == NULL || (filter->ipf_input == NULL && filter->ipf_output == NULL))
121		return EINVAL;
122
123	MALLOC(new_filter, struct ipfilter*, sizeof(*new_filter), M_IFADDR, M_WAITOK);
124	if (new_filter == NULL)
125		return ENOMEM;
126
127	lck_mtx_lock(kipf_lock);
128	new_filter->ipf_filter = *filter;
129	new_filter->ipf_head = head;
130
131	TAILQ_INSERT_HEAD(head, new_filter, ipf_link);
132
133	lck_mtx_unlock(kipf_lock);
134
135	*filter_ref = (ipfilter_t)new_filter;
136
137	/* This will force TCP to re-evaluate its use of TSO */
138	OSAddAtomic(1, &kipf_count);
139	routegenid_update();
140
141	return 0;
142}
143
144errno_t
145ipf_addv4(
146	const struct ipf_filter* filter,
147	ipfilter_t *filter_ref)
148{
149	return ipf_add(filter, filter_ref, &ipv4_filters);
150}
151
152errno_t
153ipf_addv6(
154	const struct ipf_filter* filter,
155	ipfilter_t *filter_ref)
156{
157	return ipf_add(filter, filter_ref, &ipv6_filters);
158}
159
160errno_t
161ipf_remove(
162	ipfilter_t filter_ref)
163{
164	struct ipfilter	*match = (struct ipfilter*)filter_ref;
165	struct ipfilter_list *head;
166
167	if (match == 0 || (match->ipf_head != &ipv4_filters && match->ipf_head != &ipv6_filters))
168		return EINVAL;
169
170	head = match->ipf_head;
171
172	lck_mtx_lock(kipf_lock);
173	TAILQ_FOREACH(match, head, ipf_link) {
174		if (match == (struct ipfilter*)filter_ref) {
175			ipf_detach_func ipf_detach = match->ipf_filter.ipf_detach;
176			void* cookie = match->ipf_filter.cookie;
177
178			/*
179			 * Cannot detach when they are filters running
180			 */
181			if (kipf_ref) {
182				kipf_delayed_remove++;
183				TAILQ_INSERT_TAIL(&tbr_filters, match, ipf_tbr);
184				match->ipf_filter.ipf_input = 0;
185				match->ipf_filter.ipf_output = 0;
186				lck_mtx_unlock(kipf_lock);
187			} else {
188				TAILQ_REMOVE(head, match, ipf_link);
189				lck_mtx_unlock(kipf_lock);
190				if (ipf_detach)
191					ipf_detach(cookie);
192				FREE(match, M_IFADDR);
193
194				/* This will force TCP to re-evaluate its use of TSO */
195				OSAddAtomic(-1, &kipf_count);
196				routegenid_update();
197
198			}
199			return 0;
200		}
201	}
202	lck_mtx_unlock(kipf_lock);
203
204	return ENOENT;
205}
206
207int log_for_en1 = 0;
208
209errno_t
210ipf_inject_input(
211	mbuf_t data,
212	ipfilter_t filter_ref)
213{
214	struct mbuf	*m = (struct mbuf*)data;
215	struct m_tag *mtag = 0;
216	struct ip *ip = mtod(m, struct ip *);
217	u_int8_t	vers;
218	int hlen;
219	errno_t error = 0;
220	protocol_family_t proto;
221
222	vers = IP_VHL_V(ip->ip_vhl);
223
224	switch (vers) {
225		case 4:
226			proto = PF_INET;
227			break;
228		case 6:
229			proto = PF_INET6;
230			break;
231		default:
232			error = ENOTSUP;
233			goto done;
234	}
235
236	if (filter_ref == 0 && m->m_pkthdr.rcvif == 0) {
237		m->m_pkthdr.rcvif = lo_ifp;
238		m->m_pkthdr.csum_data = 0;
239		m->m_pkthdr.csum_flags = 0;
240		if (vers == 4) {
241			hlen = IP_VHL_HL(ip->ip_vhl) << 2;
242			ip->ip_sum = 0;
243			ip->ip_sum = in_cksum(m, hlen);
244		}
245	}
246	if (filter_ref != 0) {
247		mtag = m_tag_create(KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_IPFILT,
248					 	   sizeof (ipfilter_t), M_NOWAIT, m);
249		if (mtag == NULL) {
250			error = ENOMEM;
251			goto done;
252		}
253		*(ipfilter_t*)(mtag+1) = filter_ref;
254		m_tag_prepend(m, mtag);
255	}
256
257	error = proto_inject(proto, data);
258
259done:
260	return error;
261}
262
263static errno_t
264ipf_injectv4_out(mbuf_t data, ipfilter_t filter_ref, ipf_pktopts_t options)
265{
266	struct route ro;
267	struct ip	*ip;
268	struct mbuf	*m = (struct mbuf*)data;
269	errno_t error = 0;
270	struct m_tag *mtag = NULL;
271	struct ip_moptions *imo = NULL;
272	struct ip_out_args ipoa = { IFSCOPE_NONE, { 0 }, 0, 0 };
273
274	/* Make the IP header contiguous in the mbuf */
275	if ((size_t)m->m_len < sizeof (struct ip)) {
276		m = m_pullup(m, sizeof (struct ip));
277		if (m == NULL)
278			return (ENOMEM);
279	}
280	ip = (struct ip *)m_mtod(m);
281
282	if (filter_ref != 0) {
283		mtag = m_tag_create(KERNEL_MODULE_TAG_ID,
284		    KERNEL_TAG_TYPE_IPFILT, sizeof (ipfilter_t), M_NOWAIT, m);
285		if (mtag == NULL) {
286			m_freem(m);
287			return (ENOMEM);
288		}
289		*(ipfilter_t *)(mtag + 1) = filter_ref;
290		m_tag_prepend(m, mtag);
291	}
292
293	if (options != NULL && (options->ippo_flags & IPPOF_MCAST_OPTS) &&
294	    (imo = ip_allocmoptions(M_DONTWAIT)) != NULL) {
295		imo->imo_multicast_ifp = options->ippo_mcast_ifnet;
296		imo->imo_multicast_ttl = options->ippo_mcast_ttl;
297		imo->imo_multicast_loop = options->ippo_mcast_loop;
298	}
299
300	if (options != NULL) {
301		if (options->ippo_flags & IPPOF_SELECT_SRCIF)
302			ipoa.ipoa_flags |= IPOAF_SELECT_SRCIF;
303		if (options->ippo_flags & IPPOF_BOUND_IF) {
304			ipoa.ipoa_flags |= IPOAF_BOUND_IF;
305			ipoa.ipoa_boundif = options->ippo_flags >>
306			    IPPOF_SHIFT_IFSCOPE;
307		}
308		if (options->ippo_flags & IPPOF_NO_IFT_CELLULAR)
309			ipoa.ipoa_flags |= IPOAF_NO_CELLULAR;
310		if (options->ippo_flags & IPPOF_BOUND_SRCADDR)
311			ipoa.ipoa_flags |= IPOAF_BOUND_SRCADDR;
312	}
313
314	bzero(&ro, sizeof(struct route));
315
316	/* Put ip_len and ip_off in host byte order, ip_output expects that */
317
318#if BYTE_ORDER != BIG_ENDIAN
319	NTOHS(ip->ip_len);
320	NTOHS(ip->ip_off);
321#endif
322
323	/* Send; enforce source interface selection via IP_OUTARGS flag */
324	error = ip_output(m, NULL, &ro,
325	    IP_ALLOWBROADCAST | IP_RAWOUTPUT | IP_OUTARGS, imo, &ipoa);
326
327	/* Release the route */
328	ROUTE_RELEASE(&ro);
329
330	if (imo != NULL)
331		IMO_REMREF(imo);
332
333	return (error);
334}
335
336#if INET6
337static errno_t
338ipf_injectv6_out(mbuf_t data, ipfilter_t filter_ref, ipf_pktopts_t options)
339{
340	struct route_in6 ro;
341	struct ip6_hdr	*ip6;
342	struct mbuf	*m = (struct mbuf*)data;
343	errno_t error = 0;
344	struct m_tag *mtag = NULL;
345	struct ip6_moptions *im6o = NULL;
346	struct ip6_out_args ip6oa = { IFSCOPE_NONE, { 0 }, 0, 0 };
347
348	/* Make the IP header contiguous in the mbuf */
349	if ((size_t)m->m_len < sizeof(struct ip6_hdr)) {
350		m = m_pullup(m, sizeof(struct ip6_hdr));
351		if (m == NULL)
352			return (ENOMEM);
353	}
354	ip6 = (struct ip6_hdr*)m_mtod(m);
355
356	if (filter_ref != 0) {
357		mtag = m_tag_create(KERNEL_MODULE_TAG_ID,
358		    KERNEL_TAG_TYPE_IPFILT, sizeof (ipfilter_t), M_NOWAIT, m);
359		if (mtag == NULL) {
360			m_freem(m);
361			return (ENOMEM);
362		}
363		*(ipfilter_t *)(mtag + 1) = filter_ref;
364		m_tag_prepend(m, mtag);
365	}
366
367	if (options != NULL && (options->ippo_flags & IPPOF_MCAST_OPTS) &&
368	    (im6o = ip6_allocmoptions(M_DONTWAIT)) != NULL) {
369		im6o->im6o_multicast_ifp = options->ippo_mcast_ifnet;
370		im6o->im6o_multicast_hlim = options->ippo_mcast_ttl;
371		im6o->im6o_multicast_loop = options->ippo_mcast_loop;
372	}
373
374	if (options != NULL) {
375		if (options->ippo_flags & IPPOF_SELECT_SRCIF)
376			ip6oa.ip6oa_flags |= IP6OAF_SELECT_SRCIF;
377		if (options->ippo_flags & IPPOF_BOUND_IF) {
378			ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF;
379			ip6oa.ip6oa_boundif = options->ippo_flags >>
380			    IPPOF_SHIFT_IFSCOPE;
381		}
382		if (options->ippo_flags & IPPOF_NO_IFT_CELLULAR)
383			ip6oa.ip6oa_flags |= IP6OAF_NO_CELLULAR;
384		if (options->ippo_flags & IPPOF_BOUND_SRCADDR)
385			ip6oa.ip6oa_flags |= IP6OAF_BOUND_SRCADDR;
386	}
387
388	bzero(&ro, sizeof(struct route_in6));
389
390	/*
391	 * Send  mbuf and ifscope information. Check for correctness
392	 * of ifscope information is done while searching for a route in
393	 * ip6_output.
394	 */
395	error = ip6_output(m, NULL, &ro, IPV6_OUTARGS, im6o, NULL, &ip6oa);
396
397	/* Release the route */
398	ROUTE_RELEASE(&ro);
399
400	if (im6o != NULL)
401		IM6O_REMREF(im6o);
402
403	return (error);
404}
405#endif /* INET6 */
406
407errno_t
408ipf_inject_output(
409	mbuf_t data,
410	ipfilter_t filter_ref,
411	ipf_pktopts_t options)
412{
413	struct mbuf	*m = (struct mbuf*)data;
414	u_int8_t	vers;
415	errno_t		error = 0;
416
417	/* Make one byte of the header contiguous in the mbuf */
418	if (m->m_len < 1) {
419		m = m_pullup(m, 1);
420		if (m == NULL)
421			goto done;
422	}
423
424	vers = (*(u_int8_t*)m_mtod(m)) >> 4;
425	switch (vers)
426	{
427		case 4:
428			error = ipf_injectv4_out(data, filter_ref, options);
429			break;
430#if INET6
431		case 6:
432			error = ipf_injectv6_out(data, filter_ref, options);
433			break;
434#endif
435		default:
436			m_freem(m);
437			error = ENOTSUP;
438			break;
439	}
440
441done:
442	return error;
443}
444
445__private_extern__ ipfilter_t
446ipf_get_inject_filter(struct mbuf *m)
447{
448	ipfilter_t filter_ref = 0;
449	struct m_tag *mtag;
450
451	mtag = m_tag_locate(m, KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_IPFILT, NULL);
452	if (mtag) {
453		filter_ref = *(ipfilter_t *)(mtag+1);
454
455		m_tag_delete(m, mtag);
456	}
457	return filter_ref;
458}
459
460__private_extern__ int
461ipf_init(void)
462{
463	int error = 0;
464	lck_grp_attr_t *grp_attributes = 0;
465	lck_attr_t *lck_attributes = 0;
466	lck_grp_t *lck_grp = 0;
467
468	grp_attributes = lck_grp_attr_alloc_init();
469	if (grp_attributes == 0) {
470		printf("ipf_init: lck_grp_attr_alloc_init failed\n");
471		error = ENOMEM;
472		goto done;
473	}
474
475	lck_grp = lck_grp_alloc_init("IP Filter", grp_attributes);
476	if (lck_grp == 0) {
477		printf("ipf_init: lck_grp_alloc_init failed\n");
478		error = ENOMEM;
479		goto done;
480	}
481
482	lck_attributes = lck_attr_alloc_init();
483	if (lck_attributes == 0) {
484		printf("ipf_init: lck_attr_alloc_init failed\n");
485		error = ENOMEM;
486		goto done;
487	}
488
489	lck_mtx_init(kipf_lock, lck_grp, lck_attributes);
490
491	done:
492	if (lck_grp) {
493		lck_grp_free(lck_grp);
494		lck_grp = 0;
495	}
496	if (grp_attributes) {
497		lck_grp_attr_free(grp_attributes);
498		grp_attributes = 0;
499	}
500	if (lck_attributes) {
501		lck_attr_free(lck_attributes);
502		lck_attributes = 0;
503	}
504
505	return error;
506}
507