2  * Copyright (c) 2008 Apple Computer, Inc. All rights reserved. 
   4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 
   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. 
  15  * Please obtain a copy of the License at 
  16  * http://www.opensource.apple.com/apsl/ and read it before using this file. 
  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. 
  26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 
  29 #include <sys/param.h>  /* for definition of NULL */ 
  30 #include <sys/errno.h> 
  31 #include <sys/malloc.h> 
  32 #include <sys/socket.h> 
  34 #include <sys/systm.h> 
  35 #include <libkern/OSAtomic.h> 
  37 #include <machine/endian.h> 
  40 #include <net/if_var.h> 
  41 #include <net/route.h> 
  42 #include <net/kpi_protocol.h> 
  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> 
  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 
  62 static lck_mtx_t 
*kipf_lock 
= 0; 
  63 static u_int32_t kipf_ref 
= 0; 
  64 static u_int32_t kipf_delayed_remove 
= 0; 
  65 u_int32_t kipf_count 
= 0; 
  67 __private_extern__ 
struct ipfilter_list ipv4_filters 
= TAILQ_HEAD_INITIALIZER(ipv4_filters
); 
  68 __private_extern__ 
struct ipfilter_list ipv6_filters 
= TAILQ_HEAD_INITIALIZER(ipv6_filters
); 
  69 __private_extern__ 
struct ipfilter_list tbr_filters 
= TAILQ_HEAD_INITIALIZER(tbr_filters
); 
  71 __private_extern__ 
void 
  74         lck_mtx_lock(kipf_lock
); 
  76         lck_mtx_unlock(kipf_lock
); 
  79 __private_extern__ 
void 
  82         lck_mtx_lock(kipf_lock
); 
  85         panic("ipf_unref: kipf_ref == 0\n"); 
  88     if (kipf_ref 
== 0 && kipf_delayed_remove 
!= 0) { 
  89         struct ipfilter 
*filter
; 
  91                 while ((filter 
= TAILQ_FIRST(&tbr_filters
))) { 
  92                         ipf_detach_func ipf_detach 
= filter
->ipf_filter
.ipf_detach
; 
  93                         void* cookie 
= filter
->ipf_filter
.cookie
; 
  95                         TAILQ_REMOVE(filter
->ipf_head
, filter
, ipf_link
); 
  96                         TAILQ_REMOVE(&tbr_filters
, filter
, ipf_tbr
); 
  97                         kipf_delayed_remove
--; 
 100                                 lck_mtx_unlock(kipf_lock
); 
 102                                 lck_mtx_lock(kipf_lock
); 
 103                                 /* In case some filter got to run while we released the lock */ 
 109         lck_mtx_unlock(kipf_lock
); 
 114         const struct ipf_filter
* filter
, 
 115         ipfilter_t 
*filter_ref
, 
 116         struct ipfilter_list 
*head
) 
 118         struct ipfilter 
*new_filter
; 
 119         if (filter
->name 
== NULL 
|| (filter
->ipf_input 
== NULL 
&& filter
->ipf_output 
== NULL
)) 
 122         MALLOC(new_filter
, struct ipfilter
*, sizeof(*new_filter
), M_IFADDR
, M_WAITOK
); 
 123         if (new_filter 
== NULL
) 
 126         lck_mtx_lock(kipf_lock
); 
 127         new_filter
->ipf_filter 
= *filter
; 
 128         new_filter
->ipf_head 
= head
; 
 130         TAILQ_INSERT_HEAD(head
, new_filter
, ipf_link
); 
 132         lck_mtx_unlock(kipf_lock
); 
 134         *filter_ref 
= (ipfilter_t
)new_filter
; 
 136         /* This will force TCP to re-evaluate its use of TSO */ 
 137         OSAddAtomic(1, &kipf_count
); 
 146         const struct ipf_filter
* filter
, 
 147         ipfilter_t 
*filter_ref
) 
 149         return ipf_add(filter
, filter_ref
, &ipv4_filters
); 
 154         const struct ipf_filter
* filter
, 
 155         ipfilter_t 
*filter_ref
) 
 157         return ipf_add(filter
, filter_ref
, &ipv6_filters
); 
 162         ipfilter_t filter_ref
) 
 164         struct ipfilter 
*match 
= (struct ipfilter
*)filter_ref
; 
 165         struct ipfilter_list 
*head
; 
 167         if (match 
== 0 || (match
->ipf_head 
!= &ipv4_filters 
&& match
->ipf_head 
!= &ipv6_filters
)) 
 170         head 
= match
->ipf_head
; 
 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
; 
 179                          * Cannot detach when they are filters running 
 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
); 
 188                                 TAILQ_REMOVE(head
, match
, ipf_link
); 
 189                                 lck_mtx_unlock(kipf_lock
); 
 192                                 FREE(match
, M_IFADDR
); 
 194                                 /* This will force TCP to re-evaluate its use of TSO */ 
 195                                 OSAddAtomic(-1, &kipf_count
); 
 203         lck_mtx_unlock(kipf_lock
); 
 213         ipfilter_t filter_ref
) 
 215         struct mbuf     
*m 
= (struct mbuf
*)data
; 
 216         struct m_tag 
*mtag 
= 0; 
 217         struct ip 
*ip 
= mtod(m
, struct ip 
*); 
 221         protocol_family_t proto
; 
 223         vers 
= IP_VHL_V(ip
->ip_vhl
); 
 237         if (filter_ref 
== 0 && m
->m_pkthdr
.rcvif 
== 0) { 
 238                 m
->m_pkthdr
.rcvif 
= ifunit("lo0"); 
 239                 m
->m_pkthdr
.csum_data 
= 0; 
 240                 m
->m_pkthdr
.csum_flags 
= 0; 
 242                         hlen 
= IP_VHL_HL(ip
->ip_vhl
) << 2; 
 244                         ip
->ip_sum 
= in_cksum(m
, hlen
); 
 247         if (filter_ref 
!= 0) { 
 248                 mtag 
= m_tag_alloc(KERNEL_MODULE_TAG_ID
, KERNEL_TAG_TYPE_IPFILT
, 
 249                                                    sizeof (ipfilter_t
), M_NOWAIT
); 
 254                 *(ipfilter_t
*)(mtag
+1) = filter_ref
; 
 255                 m_tag_prepend(m
, mtag
); 
 258         error 
= proto_inject(proto
, data
); 
 267         ipfilter_t filter_ref
, 
 268         ipf_pktopts_t options
) 
 271         struct sockaddr_in      
*sin 
= (struct sockaddr_in
*)&ro
.ro_dst
; 
 273         struct mbuf     
*m 
= (struct mbuf
*)data
; 
 275         struct m_tag 
*mtag 
= 0; 
 276         struct ip_moptions 
*imo 
= 0, ip_moptions
; 
 278         /* Make the IP header contiguous in the mbuf */ 
 279         if ((size_t)m
->m_len 
< sizeof(struct ip
)) { 
 280                 m 
= m_pullup(m
, sizeof(struct ip
)); 
 281                 if (m 
== NULL
) return ENOMEM
; 
 283         ip 
= (struct ip
*)m_mtod(m
); 
 285         if (filter_ref 
!= 0) { 
 286                 mtag 
= m_tag_alloc(KERNEL_MODULE_TAG_ID
, KERNEL_TAG_TYPE_IPFILT
, 
 287                                                    sizeof (ipfilter_t
), M_NOWAIT
); 
 292                 *(ipfilter_t
*)(mtag
+1) = filter_ref
; 
 293                 m_tag_prepend(m
, mtag
); 
 296         if (options 
&& (options
->ippo_flags 
& IPPOF_MCAST_OPTS
)) { 
 299                 bzero(imo
, sizeof(struct ip6_moptions
)); 
 300                 imo
->imo_multicast_ifp 
= options
->ippo_mcast_ifnet
; 
 301                 imo
->imo_multicast_ttl 
= options
->ippo_mcast_ttl
; 
 302                 imo
->imo_multicast_loop 
= options
->ippo_mcast_loop
; 
 305         /* Fill out a route structure and get a route */ 
 306         bzero(&ro
, sizeof(struct route
)); 
 307         sin
->sin_len 
= sizeof(struct sockaddr_in
); 
 308         sin
->sin_family 
= AF_INET
; 
 310         sin
->sin_addr 
= ip
->ip_dst
; 
 312         if (ro
.ro_rt 
== NULL
) { 
 317         /* Put ip_len and ip_off in host byte order, ip_output expects that */ 
 319 #if BYTE_ORDER != BIG_ENDIAN 
 325         error 
= ip_output(m
, NULL
, &ro
, IP_ALLOWBROADCAST 
| IP_RAWOUTPUT
, imo
, NULL
); 
 327         /* Release the route */ 
 338         ipfilter_t filter_ref
, 
 339         ipf_pktopts_t options
) 
 342         struct sockaddr_in6     
*sin6 
= &ro
.ro_dst
; 
 344         struct mbuf     
*m 
= (struct mbuf
*)data
; 
 346         struct m_tag 
*mtag 
= 0; 
 347         struct ip6_moptions 
*im6o 
= 0, ip6_moptions
; 
 349         /* Make the IP header contiguous in the mbuf */ 
 350         if ((size_t)m
->m_len 
< sizeof(struct ip6_hdr
)) { 
 351                 m 
= m_pullup(m
, sizeof(struct ip6_hdr
)); 
 352                 if (m 
== NULL
) return ENOMEM
; 
 354         ip6 
= (struct ip6_hdr
*)m_mtod(m
); 
 356         if (filter_ref 
!= 0) { 
 357                 mtag 
= m_tag_alloc(KERNEL_MODULE_TAG_ID
, KERNEL_TAG_TYPE_IPFILT
, 
 358                                                    sizeof (ipfilter_t
), M_NOWAIT
); 
 363                 *(ipfilter_t
*)(mtag
+1) = filter_ref
; 
 364                 m_tag_prepend(m
, mtag
); 
 367         if (options 
&& (options
->ippo_flags 
& IPPOF_MCAST_OPTS
)) { 
 368                 im6o 
= &ip6_moptions
; 
 370                 bzero(im6o
, sizeof(struct ip6_moptions
)); 
 371                 im6o
->im6o_multicast_ifp 
= options
->ippo_mcast_ifnet
; 
 372                 im6o
->im6o_multicast_hlim 
= options
->ippo_mcast_ttl
; 
 373                 im6o
->im6o_multicast_loop 
= options
->ippo_mcast_loop
; 
 377         /* Fill out a route structure and get a route */ 
 378         bzero(&ro
, sizeof(struct route_in6
)); 
 379         sin6
->sin6_len 
= sizeof(struct sockaddr_in6
); 
 380         sin6
->sin6_family 
= AF_INET6
; 
 381         sin6
->sin6_addr 
= ip6
->ip6_dst
; 
 383         /* This is breaks loopback multicast! */ 
 384         /* The scope ID should already at s6_addr16[1] */ 
 385         if (IN6_IS_SCOPE_LINKLOCAL(&ip6
->ip6_dst
)) { 
 386                 /* Hack, pull the scope_id out of the dest addr */ 
 387                 sin6
->sin6_scope_id 
= ntohs(ip6
->ip6_dst
.s6_addr16
[1]); 
 388                 ip6
->ip6_dst
.s6_addr16
[1] = 0; 
 390                 sin6
->sin6_scope_id 
= 0; 
 392         rtalloc((struct route
*)&ro
); 
 393         if (ro
.ro_rt 
== NULL
) { 
 399         error 
= ip6_output(m
, NULL
, &ro
, 0, im6o
, NULL
, 0); 
 401         /* Release the route */ 
 412         ipfilter_t filter_ref
, 
 413         ipf_pktopts_t options
) 
 415         struct mbuf     
*m 
= (struct mbuf
*)data
; 
 419         /* Make one byte of the header contiguous in the mbuf */ 
 426         vers 
= (*(u_int8_t
*)m_mtod(m
)) >> 4; 
 430                         error 
= ipf_injectv4_out(data
, filter_ref
, options
); 
 434                         error 
= ipf_injectv6_out(data
, filter_ref
, options
); 
 447 __private_extern__ ipfilter_t
 
 448 ipf_get_inject_filter(struct mbuf 
*m
) 
 450         ipfilter_t filter_ref 
= 0; 
 453         mtag 
= m_tag_locate(m
, KERNEL_MODULE_TAG_ID
, KERNEL_TAG_TYPE_IPFILT
, NULL
); 
 455                 filter_ref 
= *(ipfilter_t 
*)(mtag
+1); 
 457                 m_tag_delete(m
, mtag
); 
 462 __private_extern__ 
int 
 466         lck_grp_attr_t 
*grp_attributes 
= 0; 
 467         lck_attr_t 
*lck_attributes 
= 0; 
 468         lck_grp_t 
*lck_grp 
= 0; 
 470         grp_attributes 
= lck_grp_attr_alloc_init(); 
 471         if (grp_attributes 
== 0) { 
 472                 printf("ipf_init: lck_grp_attr_alloc_init failed\n"); 
 477         lck_grp 
= lck_grp_alloc_init("IP Filter", grp_attributes
); 
 479                 printf("ipf_init: lck_grp_alloc_init failed\n"); 
 484         lck_attributes 
= lck_attr_alloc_init(); 
 485         if (lck_attributes 
== 0) { 
 486                 printf("ipf_init: lck_attr_alloc_init failed\n"); 
 491         kipf_lock 
= lck_mtx_alloc_init(lck_grp
, lck_attributes
); 
 492         if (kipf_lock 
== 0) { 
 493                 printf("ipf_init: lck_mtx_alloc_init failed\n"); 
 500                         lck_mtx_free(kipf_lock
, lck_grp
); 
 505                 lck_grp_free(lck_grp
); 
 508         if (grp_attributes
) { 
 509                 lck_grp_attr_free(grp_attributes
); 
 512         if (lck_attributes
) { 
 513                 lck_attr_free(lck_attributes
);