Print this page
OS-5007 support SO_ATTACH_FILTER on ICMP sockets
Reviewed by: Cody Mello <melloc@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Approved by: Jerry Jelinek <jerry.jelinek@joyent.com>

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/inet/ip/icmp.c
          +++ new/usr/src/uts/common/inet/ip/icmp.c
↓ open down ↓ 14 lines elided ↑ open up ↑
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  23   23   * Copyright (c) 2013 by Delphix. All rights reserved.
  24   24   * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
       25 + * Copyright 2016 Joyent, Inc.
  25   26   */
  26   27  /* Copyright (c) 1990 Mentat Inc. */
  27   28  
  28   29  #include <sys/types.h>
  29   30  #include <sys/stream.h>
  30   31  #include <sys/stropts.h>
  31   32  #include <sys/strlog.h>
  32   33  #include <sys/strsun.h>
  33   34  #define _SUN_TPI_VERSION 2
  34   35  #include <sys/tihdr.h>
↓ open down ↓ 38 lines elided ↑ open up ↑
  73   74  #include <inet/nd.h>
  74   75  #include <inet/optcom.h>
  75   76  #include <inet/snmpcom.h>
  76   77  #include <inet/kstatcom.h>
  77   78  #include <inet/ipclassifier.h>
  78   79  
  79   80  #include <sys/tsol/label.h>
  80   81  #include <sys/tsol/tnet.h>
  81   82  
  82   83  #include <inet/rawip_impl.h>
       84 +#include <net/bpf.h>
  83   85  
  84   86  #include <sys/disp.h>
  85   87  
  86   88  /*
  87   89   * Synchronization notes:
  88   90   *
  89   91   * RAWIP is MT and uses the usual kernel synchronization primitives. We use
  90   92   * conn_lock to protect the icmp_t.
  91   93   *
  92   94   * Plumbing notes:
↓ open down ↓ 911 lines elided ↑ open up ↑
1004 1006  static void
1005 1007  icmp_close_free(conn_t *connp)
1006 1008  {
1007 1009          icmp_t *icmp = connp->conn_icmp;
1008 1010  
1009 1011          if (icmp->icmp_filter != NULL) {
1010 1012                  kmem_free(icmp->icmp_filter, sizeof (icmp6_filter_t));
1011 1013                  icmp->icmp_filter = NULL;
1012 1014          }
1013 1015  
     1016 +        if (icmp->icmp_bpf_len != 0) {
     1017 +                kmem_free(icmp->icmp_bpf_prog, icmp->icmp_bpf_len);
     1018 +                icmp->icmp_bpf_len = 0;
     1019 +                icmp->icmp_bpf_prog = NULL;
     1020 +        }
     1021 +
1014 1022          /*
1015 1023           * Clear any fields which the kmem_cache constructor clears.
1016 1024           * Only icmp_connp needs to be preserved.
1017 1025           * TBD: We should make this more efficient to avoid clearing
1018 1026           * everything.
1019 1027           */
1020 1028          ASSERT(icmp->icmp_connp == connp);
1021 1029          bzero(icmp, sizeof (icmp_t));
1022 1030          icmp->icmp_connp = connp;
1023 1031  }
↓ open down ↓ 933 lines elided ↑ open up ↑
1957 1965  int
1958 1966  icmp_tpi_opt_get(queue_t *q, int level, int name, uchar_t *ptr)
1959 1967  {
1960 1968          conn_t          *connp = Q_TO_CONN(q);
1961 1969          int             err;
1962 1970  
1963 1971          err = icmp_opt_get(connp, level, name, ptr);
1964 1972          return (err);
1965 1973  }
1966 1974  
     1975 +static int
     1976 +icmp_attach_filter(icmp_t *icmp, uint_t inlen, const uchar_t *invalp)
     1977 +{
     1978 +        struct bpf_program prog;
     1979 +        ip_bpf_insn_t *insns = NULL;
     1980 +        unsigned int size;
     1981 +
     1982 +#ifdef _LP64
     1983 +        if (get_udatamodel() != DATAMODEL_NATIVE) {
     1984 +                struct bpf_program32 *prog32;
     1985 +
     1986 +                if (inlen != sizeof (struct bpf_program32)) {
     1987 +                        return (EINVAL);
     1988 +                }
     1989 +                prog32 = (struct bpf_program32 *)invalp;
     1990 +                prog.bf_len = prog32->bf_len;
     1991 +                prog.bf_insns = (void *)(uint64_t)prog32->bf_insns;
     1992 +        } else
     1993 +#endif
     1994 +        if (inlen == sizeof (struct bpf_program)) {
     1995 +                bcopy(invalp, &prog, sizeof (prog));
     1996 +        } else {
     1997 +                return (EINVAL);
     1998 +        }
     1999 +
     2000 +        if (prog.bf_len > BPF_MAXINSNS || prog.bf_len == 0) {
     2001 +                return (EINVAL);
     2002 +        }
     2003 +        size = prog.bf_len * sizeof (struct bpf_insn);
     2004 +        insns = kmem_alloc(size, KM_SLEEP);
     2005 +        if (copyin(prog.bf_insns, insns, size) != 0) {
     2006 +                kmem_free(insns, size);
     2007 +                return (EFAULT);
     2008 +        }
     2009 +        if (!ip_bpf_validate(insns, prog.bf_len)) {
     2010 +                kmem_free(insns, size);
     2011 +                return (EINVAL);
     2012 +        }
     2013 +
     2014 +        rw_enter(&icmp->icmp_bpf_lock, RW_WRITER);
     2015 +        if (icmp->icmp_bpf_len != 0) {
     2016 +                ASSERT(icmp->icmp_bpf_prog != NULL);
     2017 +
     2018 +                kmem_free(icmp->icmp_bpf_prog, icmp->icmp_bpf_len);
     2019 +        }
     2020 +        icmp->icmp_bpf_len = size;
     2021 +        icmp->icmp_bpf_prog = insns;
     2022 +        rw_exit(&icmp->icmp_bpf_lock);
     2023 +        return (0);
     2024 +}
     2025 +
     2026 +static int
     2027 +icmp_detach_filter(icmp_t *icmp)
     2028 +{
     2029 +        int error;
     2030 +
     2031 +        rw_enter(&icmp->icmp_bpf_lock, RW_WRITER);
     2032 +        if (icmp->icmp_bpf_len == 0) {
     2033 +                ASSERT(icmp->icmp_bpf_prog == NULL);
     2034 +                error = ENOENT;
     2035 +        } else {
     2036 +                kmem_free(icmp->icmp_bpf_prog,
     2037 +                    icmp->icmp_bpf_len);
     2038 +                icmp->icmp_bpf_len = 0;
     2039 +                icmp->icmp_bpf_prog = NULL;
     2040 +                error = 0;
     2041 +        }
     2042 +        rw_exit(&icmp->icmp_bpf_lock);
     2043 +        return (error);
     2044 +}
     2045 +
     2046 +static boolean_t
     2047 +icmp_eval_filter(icmp_t *icmp, mblk_t *mp, ip_recv_attr_t *ira)
     2048 +{
     2049 +        boolean_t res;
     2050 +        uchar_t *buf = mp->b_rptr;
     2051 +        uint_t wirelen, len = MBLKL(mp);
     2052 +
     2053 +        rw_enter(&icmp->icmp_bpf_lock, RW_READER);
     2054 +        if (icmp->icmp_bpf_len == 0) {
     2055 +                rw_exit(&icmp->icmp_bpf_lock);
     2056 +                return (B_FALSE);
     2057 +        }
     2058 +        if (ira->ira_flags & IRAF_IS_IPV4) {
     2059 +                ipha_t *ipha = (ipha_t *)buf;
     2060 +
     2061 +                wirelen = ntohs(ipha->ipha_length);
     2062 +        } else {
     2063 +                ip6_t *ip6h = (ip6_t *)buf;
     2064 +
     2065 +                wirelen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN;
     2066 +        }
     2067 +        res = !ip_bpf_filter(icmp->icmp_bpf_prog, buf, wirelen, len);
     2068 +        rw_exit(&icmp->icmp_bpf_lock);
     2069 +
     2070 +        return (res);
     2071 +}
     2072 +
1967 2073  /*
1968 2074   * This routine sets socket options.
1969 2075   */
1970 2076  int
1971 2077  icmp_do_opt_set(conn_opt_arg_t *coa, int level, int name,
1972 2078      uint_t inlen, uchar_t *invalp, cred_t *cr, boolean_t checkonly)
1973 2079  {
1974 2080          conn_t          *connp = coa->coa_connp;
1975 2081          ip_xmit_attr_t  *ixa = coa->coa_ixa;
1976 2082          icmp_t          *icmp = connp->conn_icmp;
↓ open down ↓ 69 lines elided ↑ open up ↑
2046 2152                  case SO_SNDBUF:
2047 2153                          if (*i1 > is->is_max_buf) {
2048 2154                                  return (ENOBUFS);
2049 2155                          }
2050 2156                          break;
2051 2157                  case SO_RCVBUF:
2052 2158                          if (*i1 > is->is_max_buf) {
2053 2159                                  return (ENOBUFS);
2054 2160                          }
2055 2161                          break;
     2162 +                case SO_ATTACH_FILTER:
     2163 +                        return (icmp_attach_filter(icmp, inlen, invalp));
     2164 +                case SO_DETACH_FILTER:
     2165 +                        return (icmp_detach_filter(icmp));
2056 2166                  }
2057 2167                  break;
2058 2168  
2059 2169          case IPPROTO_IP:
2060 2170                  /*
2061 2171                   * Only allow IPv4 option processing on IPv4 sockets.
2062 2172                   */
2063 2173                  if (connp->conn_family != AF_INET)
2064 2174                          return (EINVAL);
2065 2175  
↓ open down ↓ 525 lines elided ↑ open up ↑
2591 2701          mutex_enter(&connp->conn_lock);
2592 2702          recv_ancillary = connp->conn_recv_ancillary;
2593 2703          mutex_exit(&connp->conn_lock);
2594 2704  
2595 2705          ip_hdr_length = ira->ira_ip_hdr_length;
2596 2706          ASSERT(MBLKL(mp) >= ip_hdr_length);     /* IP did a pullup */
2597 2707  
2598 2708          /* Initialize regardless of IP version */
2599 2709          ipps.ipp_fields = 0;
2600 2710  
     2711 +        /* Apply socket filter, if needed */
     2712 +        if (icmp->icmp_bpf_len != 0) {
     2713 +                if (icmp_eval_filter(icmp, mp, ira)) {
     2714 +                        freemsg(mp);
     2715 +                        return;
     2716 +                }
     2717 +        }
     2718 +
2601 2719          if (ira->ira_flags & IRAF_IS_IPV4) {
2602 2720                  ASSERT(IPH_HDR_VERSION(rptr) == IPV4_VERSION);
2603 2721                  ASSERT(MBLKL(mp) >= sizeof (ipha_t));
2604 2722                  ASSERT(ira->ira_ip_hdr_length == IPH_HDR_LENGTH(rptr));
2605 2723  
2606 2724                  ipha = (ipha_t *)mp->b_rptr;
2607 2725                  if (recv_ancillary.crb_all != 0)
2608 2726                          (void) ip_find_hdr_v4(ipha, &ipps, B_FALSE);
2609 2727  
2610 2728                  /*
↓ open down ↓ 2409 lines elided ↑ open up ↑
5020 5138          kmem_free(is->is_propinfo_tbl, sizeof (icmp_propinfo_tbl));
5021 5139          is->is_propinfo_tbl = NULL;
5022 5140  
5023 5141          rawip_kstat_fini(stackid, is->is_ksp);
5024 5142          is->is_ksp = NULL;
5025 5143          ldi_ident_release(is->is_ldi_ident);
5026 5144          kmem_free(is, sizeof (*is));
5027 5145  }
5028 5146  
5029 5147  static void *
5030      -rawip_kstat_init(netstackid_t stackid) {
     5148 +rawip_kstat_init(netstackid_t stackid)
     5149 +{
5031 5150          kstat_t *ksp;
5032 5151  
5033 5152          rawip_named_kstat_t template = {
5034 5153                  { "inDatagrams",        KSTAT_DATA_UINT32, 0 },
5035 5154                  { "inCksumErrs",        KSTAT_DATA_UINT32, 0 },
5036 5155                  { "inErrors",           KSTAT_DATA_UINT32, 0 },
5037 5156                  { "outDatagrams",       KSTAT_DATA_UINT32, 0 },
5038 5157                  { "outErrors",          KSTAT_DATA_UINT32, 0 },
5039 5158          };
5040 5159  
5041 5160          ksp = kstat_create_netstack("icmp", 0, "rawip", "mib2",
5042      -                                        KSTAT_TYPE_NAMED,
5043      -                                        NUM_OF_FIELDS(rawip_named_kstat_t),
5044      -                                        0, stackid);
     5161 +            KSTAT_TYPE_NAMED, NUM_OF_FIELDS(rawip_named_kstat_t), 0, stackid);
5045 5162          if (ksp == NULL || ksp->ks_data == NULL)
5046 5163                  return (NULL);
5047 5164  
5048 5165          bcopy(&template, ksp->ks_data, sizeof (template));
5049 5166          ksp->ks_update = rawip_kstat_update;
5050 5167          ksp->ks_private = (void *)(uintptr_t)stackid;
5051 5168  
5052 5169          kstat_install(ksp);
5053 5170          return (ksp);
5054 5171  }
↓ open down ↓ 806 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX