예제 #1
1
파일: main.go 프로젝트: qmsk/nlgo
func main() {
	sk := nlgo.NlSocketAlloc()
	if err := nlgo.GenlConnect(sk); err != nil {
		panic(err)
	} else if err := nlgo.GenlSendSimple(sk, nlgo.GENL_ID_CTRL, nlgo.CTRL_CMD_GETFAMILY, nlgo.CTRL_VERSION, syscall.NLM_F_DUMP); err != nil {
		panic(err)
	}
	nl80211 := nlgo.NlSocketAlloc()
	if err := nlgo.GenlConnect(nl80211); err != nil {
		panic(err)
	}

	data := make([]byte, syscall.Getpagesize())
	if n, _, _, _, err := syscall.Recvmsg(sk.Fd, data, nil, 0); err != nil {
		panic(err)
	} else if msgs, err := syscall.ParseNetlinkMessage(data[:n]); err != nil {
		log.Print("X", err)
	} else {
		for _, msg := range msgs {
			genl := *(*nlgo.GenlMsghdr)(unsafe.Pointer(&msg.Data[0]))
			if msg.Header.Type == nlgo.GENL_ID_CTRL && genl.Cmd == nlgo.CTRL_CMD_NEWFAMILY {
				if attr, err := nlgo.CtrlPolicy.Parse(msg.Data[nlgo.GENL_HDRLEN:]); err != nil {
					log.Print(err)
				} else if amap, ok := attr.(nlgo.AttrMap); !ok {
					log.Print(attr)
				} else if value := amap.Get(nlgo.CTRL_ATTR_FAMILY_NAME).(nlgo.NulString); string(value) == "nl80211" {
					log.Printf("%v", attr)
					for _, g := range amap.Get(nlgo.CTRL_ATTR_MCAST_GROUPS).(nlgo.AttrSlice).Slice() {
						group := g.Value.(nlgo.AttrMap)
						pid := group.Get(nlgo.CTRL_ATTR_MCAST_GRP_ID).(nlgo.U32)
						if err := nlgo.NlSocketAddMembership(nl80211, int(pid)); err != nil {
							log.Print(err)
						}
					}
				}
			} else {
				log.Print("UNKNOWN")
			}
		}
	}
	nlgo.NlSocketFree(sk)

	for {
		if n, _, _, _, err := syscall.Recvmsg(nl80211.Fd, data, nil, 0); err != nil {
			panic(err)
		} else if msgs, err := syscall.ParseNetlinkMessage(data[:n]); err != nil {
			log.Print("Y", err)
		} else {
			for _, msg := range msgs {
				genl := (*nlgo.GenlMsghdr)(unsafe.Pointer(&msg.Data[0]))
				if attr, err := nlgo.Nl80211Policy.Parse(msg.Data[nlgo.GENL_HDRLEN:]); err != nil {
					log.Print("Z", err)
				} else {
					log.Printf("NL80211_CMD_%s attrs=%s", nlgo.NL80211_CMD_itoa[genl.Cmd], attr)
				}
			}
		}
	}
}
예제 #2
0
func getConnection(local *os.File) (int, error) {
	var data [4]byte
	control := make([]byte, 4*256)

	// n, oobn, recvflags, from, errno  - todo: error checking.
	_, oobn, _, _,
		err := syscall.Recvmsg(
		int(local.Fd()), data[:], control[:], 0)
	if err != nil {
		return 0, err
	}

	message := *(*syscall.Cmsghdr)(unsafe.Pointer(&control[0]))
	fd := *(*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(&control[0])) + syscall.SizeofCmsghdr))

	if message.Type != 1 {
		return 0, fmt.Errorf("getConnection: recvmsg returned wrong control type: %d", message.Type)
	}
	if oobn <= syscall.SizeofCmsghdr {
		return 0, fmt.Errorf("getConnection: too short control message. Length: %d", oobn)
	}
	if fd < 0 {
		return 0, fmt.Errorf("getConnection: fd < 0: %d", fd)
	}
	return int(fd), nil
}
예제 #3
0
파일: mount.go 프로젝트: rvijax/camlistore
func getFuseConn(local *os.File) (f *os.File, err os.Error) {
	var data [4]byte
	control := make([]byte, 4*256)

	// n, oobn, recvflags, from, errno  - todo: error checking.
	_, oobn, _, _,
		errno := syscall.Recvmsg(
		local.Fd(), data[:], control[:], 0)
	if errno != 0 {
		return
	}

	message := *(*syscall.Cmsghdr)(unsafe.Pointer(&control[0]))
	fd := *(*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(&control[0])) + syscall.SizeofCmsghdr))

	if message.Type != 1 {
		err = os.NewError(fmt.Sprintf("getFuseConn: recvmsg returned wrong control type: %d", message.Type))
		return
	}
	if oobn <= syscall.SizeofCmsghdr {
		err = os.NewError(fmt.Sprintf("getFuseConn: too short control message. Length: %d", oobn))
		return
	}
	if fd < 0 {
		err = os.NewError(fmt.Sprintf("getFuseConn: fd < 0: %d", fd))
		return
	}
	f = os.NewFile(int(fd), "<fuseConnection>")
	return
}
예제 #4
0
파일: fd_unix.go 프로젝트: duhaibo0404/go-1
func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
	if err := fd.readLock(); err != nil {
		return 0, 0, 0, nil, err
	}
	defer fd.readUnlock()
	if err := fd.pd.prepareRead(); err != nil {
		return 0, 0, 0, nil, err
	}
	for {
		n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
		if err != nil {
			// TODO(dfc) should n and oobn be set to 0
			if err == syscall.EAGAIN {
				if err = fd.pd.waitRead(); err == nil {
					continue
				}
			}
		}
		err = fd.eofError(n, err)
		break
	}
	if _, ok := err.(syscall.Errno); ok {
		err = os.NewSyscallError("recvmsg", err)
	}
	return
}
예제 #5
0
파일: fd_unix.go 프로젝트: xorrbit/golang
func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
	fd.rio.Lock()
	defer fd.rio.Unlock()
	if err := fd.incref(false); err != nil {
		return 0, 0, 0, nil, err
	}
	defer fd.decref()
	for {
		if fd.rdeadline.expired() {
			err = errTimeout
			break
		}
		n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
		if err != nil {
			// TODO(dfc) should n and oobn be set to 0
			if err == syscall.EAGAIN {
				if err = fd.pollServer.WaitRead(fd); err == nil {
					continue
				}
			}
		}
		err = chkReadErr(n, err, fd)
		break
	}
	if err != nil && err != io.EOF {
		err = &OpError{"read", fd.net, fd.laddr, err}
	}
	return
}
예제 #6
0
파일: fd_unix.go 프로젝트: hfeeki/go
func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
	fd.rio.Lock()
	defer fd.rio.Unlock()
	if err := fd.incref(false); err != nil {
		return 0, 0, 0, nil, err
	}
	defer fd.decref()
	for {
		n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
		if err == syscall.EAGAIN {
			err = errTimeout
			if fd.rdeadline >= 0 {
				if err = fd.pollServer.WaitRead(fd); err == nil {
					continue
				}
			}
		}
		if err == nil && n == 0 {
			err = io.EOF
		}
		break
	}
	if err != nil && err != io.EOF {
		err = &OpError{"read", fd.net, fd.laddr, err}
		return
	}
	return
}
예제 #7
0
파일: fd.go 프로젝트: aubonbeurre/gcc
func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
	if fd == nil || fd.sysfile == nil {
		return 0, 0, 0, nil, os.EINVAL
	}
	fd.rio.Lock()
	defer fd.rio.Unlock()
	fd.incref()
	defer fd.decref()
	if fd.rdeadline_delta > 0 {
		fd.rdeadline = pollserver.Now() + fd.rdeadline_delta
	} else {
		fd.rdeadline = 0
	}
	var oserr error
	for {
		var errno int
		n, oobn, flags, sa, errno = syscall.Recvmsg(fd.sysfd, p, oob, 0)
		if errno == syscall.EAGAIN && fd.rdeadline >= 0 {
			pollserver.WaitRead(fd)
			continue
		}
		if errno != 0 {
			oserr = os.Errno(errno)
		}
		if n == 0 {
			oserr = io.EOF
		}
		break
	}
	if oserr != nil {
		err = &OpError{"read", fd.net, fd.laddr, oserr}
		return
	}
	return
}
예제 #8
0
func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
	if fd == nil || fd.sysfile == nil {
		return 0, 0, 0, nil, os.EINVAL
	}
	fd.rio.Lock()
	defer fd.rio.Unlock()
	fd.incref()
	defer fd.decref()
	for {
		n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
		if err == syscall.EAGAIN {
			if fd.rdeadline >= 0 {
				pollserver.WaitRead(fd)
				continue
			}
			err = errTimeout
		}
		if err == nil && n == 0 {
			err = io.EOF
		}
		break
	}
	if err != nil && err != io.EOF {
		err = &OpError{"read", fd.net, fd.laddr, err}
		return
	}
	return
}
예제 #9
0
func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
	if err := fd.readLock(); err != nil {
		return 0, 0, 0, nil, err
	}
	defer fd.readUnlock()
	if err := fd.pd.PrepareRead(); err != nil {
		return 0, 0, 0, nil, &OpError{"read", fd.net, fd.laddr, err}
	}
	for {
		n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
		if err != nil {
			// TODO(dfc) should n and oobn be set to 0
			if err == syscall.EAGAIN {
				if err = fd.pd.WaitRead(); err == nil {
					continue
				}
			}
		}
		err = chkReadErr(n, err, fd)
		break
	}
	if err != nil && err != io.EOF {
		err = &OpError{"read", fd.net, fd.laddr, err}
	}
	return
}
예제 #10
0
func readMsg(file *os.File, p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
	for {
		n, oobn, flags, sa, err = syscall.Recvmsg(int(file.Fd()), p, oob, 0)
		if err != nil {
			if err == syscall.EAGAIN {
				continue
			}
		}
		break
	}
	return
}
예제 #11
0
func pfkeyReply() (spi uint32, err error) {
	buf := make([]byte, SADB_MSG_SIZE)
	if count, _, _, _, _ := syscall.Recvmsg(fd, buf, nil, syscall.MSG_PEEK); count != len(buf) {
		return spi, fmt.Errorf("incomplete sadbmsg %d %d", len(buf), count)
	}
	h := sadbMsg{}
	h.DecodeFromBytes(buf)
	if h.sadbMsgErrno != 0 {
		return spi, fmt.Errorf("sadb msg reply error %d", h.sadbMsgErrno)
	}

	if h.sadbMsgSeq != seq {
		return spi, fmt.Errorf("sadb msg sequence doesn't match %d %d", h.sadbMsgSeq, seq)
	}

	if h.sadbMsgPid != uint32(os.Getpid()) {
		return spi, fmt.Errorf("sadb msg pid doesn't match %d %d", h.sadbMsgPid, os.Getpid())
	}

	buf = make([]byte, int(8*h.sadbMsgLen))
	if count, _, _, _, _ := syscall.Recvmsg(fd, buf, nil, 0); count != len(buf) {
		return spi, fmt.Errorf("incomplete sadbmsg body %d %d", len(buf), count)
	}

	buf = buf[SADB_MSG_SIZE:]

	for len(buf) >= 4 {
		l := binary.LittleEndian.Uint16(buf[0:2]) * 8
		t := binary.LittleEndian.Uint16(buf[2:4])
		if t == SADB_EXT_SA {
			return binary.LittleEndian.Uint32(buf[4:8]), nil
		}

		if len(buf) <= int(l) {
			break
		}
		buf = buf[l:]
	}
	return spi, err
}
예제 #12
0
파일: fd.go 프로젝트: ftrvxmtrx/fd
// Get receives file descriptors from a Unix domain socket.
//
// Num specifies the expected number of file descriptors in one message.
// Internal files' names to be assigned are specified via optional filenames
// argument.
//
// You need to close all files in the returned slice. The slice can be
// non-empty even if this function returns an error.
//
// Use net.FileConn() if you're receiving a network connection.
func Get(via *net.UnixConn, num int, filenames []string) ([]*os.File, error) {
	if num < 1 {
		return nil, nil
	}

	// get the underlying socket
	viaf, err := via.File()
	if err != nil {
		return nil, err
	}
	socket := int(viaf.Fd())
	defer viaf.Close()

	// recvmsg
	buf := make([]byte, syscall.CmsgSpace(num*4))
	_, _, _, _, err = syscall.Recvmsg(socket, nil, buf, 0)
	if err != nil {
		return nil, err
	}

	// parse control msgs
	var msgs []syscall.SocketControlMessage
	msgs, err = syscall.ParseSocketControlMessage(buf)

	// convert fds to files
	res := make([]*os.File, 0, len(msgs))
	for i := 0; i < len(msgs) && err == nil; i++ {
		var fds []int
		fds, err = syscall.ParseUnixRights(&msgs[i])

		for fi, fd := range fds {
			var filename string
			if fi < len(filenames) {
				filename = filenames[fi]
			}

			res = append(res, os.NewFile(uintptr(fd), filename))
		}
	}

	return res, err
}
예제 #13
0
파일: proxy.go 프로젝트: feehoo/nginx-ssh
func handleConnection(c net.Conn, patterns []matcher) {
	var err error
	var d net.Conn
	buf := make([]byte, 4096, 4096)
	oobbuf := make([]byte, 512, 512)

	defer c.Close()

	conn := c.(*net.TCPConn)
	f, _ := conn.File()
	length, _, _, from, err := syscall.Recvmsg(int(f.Fd()), buf, oobbuf, syscall.MSG_PEEK)
	if err != nil {
		fmt.Println(from, err)
		return
	}
	f.Close()

	for n := 0; ; n += 1 {
		var host string
		var port int

		if len(patterns) == n {
			fmt.Println(c.RemoteAddr, "Protocol not recognized")
			return
		}

		host, port = patterns[n](buf, length)
		if port > 0 {
			d, err = net.Dial("tcp", fmt.Sprint(host, ":", port))
			break
		}
	}

	if err != nil {
		fmt.Println(c.RemoteAddr, err)
		return
	}
	go copyAndClose(c, d)
	io.Copy(d, c)
}
예제 #14
0
func Receive(fd int) (int, os.Error) {
	cmsglen := cmsgLen(int(unsafe.Sizeof(fd)))
	buf := make([]byte, cmsglen)
	_, _, _, _, errno := syscall.Recvmsg(fd, nil, buf, 0)
	if errno != 0 {
		return -1, fmt.Errorf("fdpass: Receive: %v", os.Errno(errno))
	}

	cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&buf[0]))
	if uint64(cmsg.Len) != uint64(cmsglen) {
		return -1, fmt.Errorf("fdpass: Receive: bad length %v", cmsg.Len)
	}
	if cmsg.Level != syscall.SOL_SOCKET {
		return -1, fmt.Errorf("fdpass: Receive: bad level %v", cmsg.Level)
	}
	if cmsg.Type != syscall.SCM_RIGHTS {
		return -1, fmt.Errorf("fdpass: Receive: bad type %v", cmsg.Type)
	}

	sendfd := *(*int)(unsafe.Pointer(&buf[cmsgLen(0)]))
	return sendfd, nil
}
예제 #15
0
파일: port.go 프로젝트: shun159/gopenflow
// Up activates packet processing.
// When the interface is down(IFF_UP is unset), opening the socket raise error.
// This is because we have to call Up each time the state changed.
func (self *NamedPort) Up() error {
	self.lock.Lock()
	defer self.lock.Unlock()
	if self.fd != -1 {
		return nil // already up
	}
	if self.flags&syscall.IFF_UP == 0 {
		return nil // not ready for up
	}
	if fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, 0); err != nil {
		panic(err)
	} else {
		self.fd = fd
	}
	if err := func() error {
		if err := syscall.SetsockoptInt(self.fd, syscall.SOL_PACKET, syscall2.PACKET_AUXDATA, 1); err != nil {
			return err
		}
		if err := syscall.Bind(self.fd, &syscall.SockaddrLinklayer{
			Protocol: syscall2.ETH_P_ALL,
			Ifindex:  int(self.ifIndex),
		}); err != nil {
			return err
		}
		if sa, err := syscall.Getsockname(self.fd); err != nil {
			return err
		} else {
			self.hatype = sa.(*syscall.SockaddrLinklayer).Hatype
			switch self.hatype {
			default:
				panic("unsupported ARPHRD")
			case syscall.ARPHRD_IEEE80211_RADIOTAP:
				// ok
			case syscall.ARPHRD_ETHER:
				// ok
			case syscall2.ARPHRD_6LOWPAN:
				// ok
			}
		}
		return nil
	}(); err != nil {
		self.Down()
		return err
	}
	go func() {
		defer self.Down()
		buf := make([]byte, 32*1024)               // enough for jumbo frame
		oob := make([]byte, syscall.CmsgSpace(20)) // msg_control, 20 = sizeof(auxdata)
		for {
			var frame Frame

			if bufN, oobN, flags, _, err := syscall.Recvmsg(self.fd, buf, oob, syscall.MSG_TRUNC); err != nil {
				if e, ok := err.(syscall.Errno); ok && e.Temporary() {
					continue
				} else {
					log.Print("Recvmsg", err)
					break
				}
			} else if bufN == 0 {
				break
			} else if bufN > len(buf) {
				log.Print("MSG_TRUNC")
			} else if flags&syscall.MSG_CTRUNC != 0 {
				log.Print("MSG_CTRUNC")
			} else {
				haveVlan := false
				var vlanTpid uint16 = 0x8100
				var vlanTci uint16
				if cmsgs, err := syscall.ParseSocketControlMessage(oob[:oobN]); err != nil {
					log.Print(err)
					return
				} else {
					for _, cmsg := range cmsgs {
						switch cmsg.Header.Type {
						case syscall2.PACKET_AUXDATA:
							aux := (*syscall2.Auxdata)(unsafe.Pointer(&cmsg.Data[0]))
							switch len(cmsg.Data) {
							case 20:
								if aux.Status&syscall2.TP_STATUS_VLAN_TPID_VALID != 0 {
									vlanTpid = aux.VlanTpid
								}
							case 18:
								// old format. pass
							default:
								log.Print("unexpected PACKET_AUXDATA")
								return
							}
							if aux.Status&syscall2.TP_STATUS_VLAN_VALID != 0 {
								haveVlan = true
								vlanTci = aux.VlanTci
							}
						}
					}
				}
				switch self.hatype {
				case syscall.ARPHRD_ETHER:
					var pkt []byte
					if haveVlan {
						pkt = make([]byte, bufN+4)
						copy(pkt[:12], buf[:12])
						binary.BigEndian.PutUint16(pkt[12:], vlanTpid)
						binary.BigEndian.PutUint16(pkt[14:], vlanTci)
						copy(pkt[16:], buf[12:bufN])
					} else {
						pkt = make([]byte, bufN)
						copy(pkt, buf)
					}
					frame = Frame{
						Data: pkt,
					}
				case syscall.ARPHRD_IEEE80211_RADIOTAP:
					// NOTE: 802.11 + PACKET_AUXDATA unsupported
					bpkt := gopacket.NewPacket(buf[:bufN], layers.LayerTypeRadioTap, gopacket.Lazy)
					if rtl := bpkt.Layer(layers.LayerTypeRadioTap); rtl == nil {
						log.Print("radiotap layer error")
					} else if rt, ok := rtl.(*layers.RadioTap); !ok {
						log.Print("radiotap layer type error")
					} else {
						if f, err := FrameFromRadiotap(rt); err != nil {
							log.Print(err)
						} else {
							frame = f
						}
					}
				case syscall2.ARPHRD_6LOWPAN:
					pkt := make([]byte, 14+bufN)
					binary.BigEndian.PutUint16(pkt[12:], 0x86DD)
					copy(pkt[14:], buf[:bufN])

					bpkt := gopacket.NewPacket(buf[:bufN], layers.LayerTypeIPv6, gopacket.Lazy)
					ip6 := bpkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
					if ip6.DstIP.IsMulticast() {
						copy(pkt, []byte{0x33, 0x33})
						copy(pkt[2:6], []byte(ip6.DstIP.To16())[12:16])
						copy(pkt[6:], self.get6lowpanMac(ip6.SrcIP))
					} else {
						copy(pkt[6:], self.get6lowpanMac(ip6.SrcIP))
						copy(pkt, self.get6lowpanMac(ip6.DstIP))
					}
					frame = Frame{
						Data: pkt,
					}
				}
				if len(frame.Data) != 0 {
					func() {
						defer func() {
							if r := recover(); r != nil {
								// this may happen in socket race condition(rtnetlink and pf_packet).
								fmt.Println("dropping packet on closed ingress.")
							}
						}()
						self.ingress <- frame
					}()
				}
			}
		}
	}()
	return nil
}
예제 #16
0
파일: rt_hub.go 프로젝트: qmsk/nlgo
func NewRtHub() (*RtHub, error) {
	self := &RtHub{
		sock:      NlSocketAlloc(),
		lock:      &sync.Mutex{},
		unicast:   make(map[uint32]chan RtMessage),
		multicast: make(map[uint32][]RtListener),
	}
	if err := NlConnect(self.sock, syscall.NETLINK_ROUTE); err != nil {
		NlSocketFree(self.sock)
		return nil, err
	}
	go func() {
		for {
			var merr error
			var messages []syscall.NetlinkMessage
			var controls []syscall.SocketControlMessage

			buf := make([]byte, syscall.Getpagesize())
			oob := make([]byte, syscall.Getpagesize())
			if bufN, oobN, _, _, err := syscall.Recvmsg(self.sock.Fd, buf, oob, syscall.MSG_TRUNC); err != nil {
				if e, ok := err.(syscall.Errno); ok && e.Temporary() {
					continue
				}
				merr = err
			} else if bufN == 0 {
				break
			} else if bufN > len(buf) {
				merr = err
			} else if oobN > len(oob) {
				merr = err
			} else if msgs, err := syscall.ParseNetlinkMessage(buf[:bufN]); err != nil {
				merr = err
			} else if ctrls, err := syscall.ParseSocketControlMessage(oob[:oobN]); err != nil {
				merr = err
			} else {
				messages = msgs
				controls = ctrls
			}
			for _, message := range messages {
				msg := RtMessage{
					Message:  message,
					Controls: controls,
					Error:    merr,
				}
				seq := message.Header.Seq
				mtype := message.Header.Type
				if seq == 0 {
					var listeners []RtListener
					self.lock.Lock()
					for _, s := range self.multicast {
						listeners = append(listeners, s...)
					}
					self.lock.Unlock()

					for _, listener := range listeners {
						listener.RtListen(msg)
					}
				} else {
					self.lock.Lock()
					listener := self.unicast[seq]
					self.lock.Unlock()

					if listener != nil {
						listener <- msg
						if mtype == syscall.NLMSG_DONE || mtype == syscall.NLMSG_ERROR {
							delete(self.unicast, seq)
							close(listener)
						}
					}
				}
			}
		}
	}()
	return self, nil
}
예제 #17
0
파일: fd_linux.go 프로젝트: hkwi/blugo
func (self HciDev) Request(opcode OpCode, params ...Parameter) (Parameters, error) {
	req := make([]byte, 4)
	req[0] = HCI_COMMAND_PKT
	binary.LittleEndian.PutUint16(req[1:], uint16(opcode))

	if pbuf, err := Parameters(params).MarshalBinary(); err != nil {
		return nil, err
	} else {
		req[3] = uint8(len(pbuf))
		req = append(req, pbuf...)
	}

	if filter, err := GetsockoptHciFilter(int(self), SOL_HCI, HCI_FILTER); err != nil {
		return nil, err
	} else {
		defer SetsockoptHciFilter(int(self), SOL_HCI, HCI_FILTER, filter)
	}

	filter := &HciFilter{
		Type_mask:  1 << HCI_EVENT_PKT,
		Event_mask: FilterEventMask(EVT_CMD_STATUS, EVT_CMD_COMPLETE),
		Opcode:     opcode.Native(),
	}
	if opcode.Ogf() == OGF_LE_CTL {
		filter.Event_mask = FilterEventMask(EVT_CMD_STATUS, EVT_CMD_COMPLETE, EVT_LE_META_EVENT)
	}
	if err := SetsockoptHciFilter(int(self), SOL_HCI, HCI_FILTER, filter); err != nil {
		return nil, err
	}

	if n, err := self.Write(req); err != nil {
		return nil, err
	} else if n != len(req) {
		return nil, fmt.Errorf("write incomplete")
	}

	if efd, err := syscall.EpollCreate1(syscall.EPOLL_CLOEXEC); err != nil {
		return nil, err
	} else {
		defer syscall.Close(efd)

		var events [1]syscall.EpollEvent
		syscall.EpollCtl(efd, syscall.EPOLL_CTL_ADD, int(self), &syscall.EpollEvent{
			Events: syscall.EPOLLIN,
			Fd:     int32(self),
		})

		capture := make([]byte, 0, 258)
		buf := make([]byte, 258)
		for {
			if n, err := syscall.EpollWait(efd, events[:], 100); err != nil {
				return nil, err
			} else if n == 0 {
				continue
			}
			// todo: operation timeout

			if n, _, _, _, err := syscall.Recvmsg(int(self), buf, nil, syscall.MSG_DONTWAIT); err != nil {
				if errno, ok := err.(syscall.Errno); ok && errno.Temporary() {
					continue
				}
				return nil, err
			} else if n == 0 {
				continue
			} else {
				capture = append(capture, buf[:n]...)
			}
			if pkt, step := Parse(capture); step == 0 {
				continue
			} else if p, err := pkt.(EventPkt).Parse(); err != nil {
				continue
			} else {
				switch ev := p.(type) {
				case EvtCmdComplete:
					if ev.OpCode == uint16(opcode) {
						return opcode.Response(ev.Params)
					}
				case EvtCmdStatus:
					if ev.OpCode == uint16(opcode) && ev.Status != 0 {
						return nil, HciError(ev.Status)
					}
				case EvtLeMetaEvent:
					// todo: OGF_LE_CTL opcode expects this
				}
				capture = capture[step:]
			}
		}
		return nil, fmt.Errorf("should not reach")
	}
}