/* * There are only two special setsockopt's for SOL_SOCKET with PF_PACKET: * SO_ATTACH_FILTER and SO_DETACH_FILTER. All other setsockopt requests * that are for SOL_SOCKET are passed back to the socket layer for its * generic implementation. * * Both of these setsockopt values are candidates for being handled by the * socket layer itself in future, however this requires understanding how * they would interact with all other sockets. */ static int pfp_setsocket_sockopt(sock_lower_handle_t handle, int option_name, const void *optval, socklen_t optlen) { struct bpf_program prog; struct bpf_insn *fcode; struct pfpsock *ps; int error = 0; int size; ps = (struct pfpsock *)handle; switch (option_name) { case SO_ATTACH_FILTER : #ifdef _LP64 if (optlen == sizeof (struct bpf_program32)) { struct bpf_program32 prog32; bcopy(optval, &prog32, sizeof (prog32)); prog.bf_len = prog32.bf_len; prog.bf_insns = (void *)(uint64_t)prog32.bf_insns; } else #endif if (optlen == sizeof (struct bpf_program)) { bcopy(optval, &prog, sizeof (prog)); } else if (optlen != sizeof (struct bpf_program)) { return (EINVAL); } size = prog.bf_len * sizeof (*prog.bf_insns); fcode = kmem_alloc(size, KM_SLEEP); if (ddi_copyin(prog.bf_insns, fcode, size, 0) != 0) { kmem_free(fcode, size); return (EFAULT); } if (bpf_validate(fcode, (int)prog.bf_len)) { rw_enter(&ps->ps_bpflock, RW_WRITER); pfp_release_bpf(ps); ps->ps_bpf.bf_insns = fcode; ps->ps_bpf.bf_len = size; rw_exit(&ps->ps_bpflock); return (0); } kmem_free(fcode, size); error = EINVAL; break; case SO_DETACH_FILTER : pfp_release_bpf(ps); break; default : /* * If sockfs code receives this error in return from the * getsockopt downcall it handles the option locally, if * it can. This implements SO_RCVBUF, etc. */ error = ENOPROTOOPT; break; } return (error); }
/* * There are only two special setsockopt's for SOL_SOCKET with PF_PACKET: * SO_ATTACH_FILTER and SO_DETACH_FILTER. * * Both of these setsockopt values are candidates for being handled by the * socket layer itself in future, however this requires understanding how * they would interact with all other sockets. */ static int pfp_setsocket_sockopt(sock_lower_handle_t handle, int option_name, const void *optval, socklen_t optlen) { struct bpf_program prog; struct bpf_insn *fcode; struct pfpsock *ps; struct sock_proto_props sopp; int error = 0; int size; ps = (struct pfpsock *)handle; switch (option_name) { case SO_ATTACH_FILTER : #ifdef _LP64 if (optlen == sizeof (struct bpf_program32)) { struct bpf_program32 prog32; bcopy(optval, &prog32, sizeof (prog32)); prog.bf_len = prog32.bf_len; prog.bf_insns = (void *)(uint64_t)prog32.bf_insns; } else #endif if (optlen == sizeof (struct bpf_program)) { bcopy(optval, &prog, sizeof (prog)); } else if (optlen != sizeof (struct bpf_program)) { return (EINVAL); } if (prog.bf_len > BPF_MAXINSNS) return (EINVAL); size = prog.bf_len * sizeof (*prog.bf_insns); fcode = kmem_alloc(size, KM_SLEEP); if (ddi_copyin(prog.bf_insns, fcode, size, 0) != 0) { kmem_free(fcode, size); return (EFAULT); } if (bpf_validate(fcode, (int)prog.bf_len)) { rw_enter(&ps->ps_bpflock, RW_WRITER); pfp_release_bpf(ps); ps->ps_bpf.bf_insns = fcode; ps->ps_bpf.bf_len = size; rw_exit(&ps->ps_bpflock); return (0); } kmem_free(fcode, size); error = EINVAL; break; case SO_DETACH_FILTER : pfp_release_bpf(ps); break; case SO_RCVBUF : size = *(int32_t *)optval; if (size > sockmod_pfp_rcvbuf_max || size < 0) return (ENOBUFS); sopp.sopp_flags = SOCKOPT_RCVHIWAT; sopp.sopp_rxhiwat = size; ps->ps_upcalls->su_set_proto_props(ps->ps_upper, &sopp); ps->ps_rcvbuf = size; break; default : error = ENOPROTOOPT; break; } return (error); }