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