| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- package nfqueue
- import (
- "context"
- "encoding/binary"
- "log"
- "github.com/florianl/go-nfqueue/internal/unix"
- "github.com/mdlayher/netlink"
- "github.com/pkg/errors"
- )
- // devNull satisfies io.Writer, in case *log.Logger is not provided
- type devNull struct{}
- func (devNull) Write(p []byte) (int, error) {
- return 0, nil
- }
- // Close the connection to the netfilter queue subsystem
- func (nfqueue *Nfqueue) Close() error {
- return nfqueue.Con.Close()
- }
- // SetVerdictWithMark signals the kernel the next action and the mark for a specified package id
- func (nfqueue *Nfqueue) SetVerdictWithMark(id uint32, verdict, mark int) error {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint32(buf, uint32(mark))
- attributes, err := netlink.MarshalAttributes([]netlink.Attribute{{
- Type: nfQaMark,
- Data: buf,
- }})
- if err != nil {
- return err
- }
- return nfqueue.setVerdict(id, verdict, false, attributes)
- }
- // SetVerdictModPacket signals the kernel the next action for an altered packet
- func (nfqueue *Nfqueue) SetVerdictModPacket(id uint32, verdict int, packet []byte) error {
- data, err := netlink.MarshalAttributes([]netlink.Attribute{{
- Type: nfQaPayload,
- Data: packet,
- }})
- if err != nil {
- return err
- }
- return nfqueue.setVerdict(id, verdict, false, data)
- }
- // SetVerdictModPacketWithMark signals the kernel the next action and mark for an altered packet
- func (nfqueue *Nfqueue) SetVerdictModPacketWithMark(id uint32, verdict, mark int, packet []byte) error {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint32(buf, uint32(mark))
- data, err := netlink.MarshalAttributes([]netlink.Attribute{{
- Type: nfQaPayload,
- Data: packet,
- },
- {Type: nfQaMark,
- Data: buf}})
- if err != nil {
- return err
- }
- return nfqueue.setVerdict(id, verdict, false, data)
- }
- // SetVerdict signals the kernel the next action for a specified package id
- func (nfqueue *Nfqueue) SetVerdict(id uint32, verdict int) error {
- return nfqueue.setVerdict(id, verdict, false, []byte{})
- }
- // SetVerdictBatch signals the kernel the next action for a batch of packages till id
- func (nfqueue *Nfqueue) SetVerdictBatch(id uint32, verdict int) error {
- return nfqueue.setVerdict(id, verdict, true, []byte{})
- }
- // Register your own function as callback for a netfilter queue
- func (nfqueue *Nfqueue) Register(ctx context.Context, fn HookFunc) error {
- return nfqueue.RegisterWithErrorFunc(ctx, fn, func(err error) int {
- if opError, ok := err.(*netlink.OpError); ok {
- if opError.Timeout() || opError.Temporary() {
- return 0
- }
- }
- nfqueue.logger.Printf("Could not receive message: %v\n", err)
- return 1
- })
- }
- // RegisterWithErrorFunc is like Register but allows custom error handling
- // for errors encountered when reading from the underlying netlink socket.
- func (nfqueue *Nfqueue) RegisterWithErrorFunc(ctx context.Context, fn HookFunc, errfn ErrorFunc) error {
- // unbinding existing handler (if any)
- seq, err := nfqueue.setConfig(unix.AF_UNSPEC, 0, 0, []netlink.Attribute{
- {Type: nfQaCfgCmd, Data: []byte{nfUlnlCfgCmdPfUnbind, 0x0, 0x0, byte(nfqueue.family)}},
- })
- if err != nil {
- return errors.Wrapf(err, "Could not unbind existing handlers (if any)")
- }
- // binding to family
- _, err = nfqueue.setConfig(unix.AF_UNSPEC, seq, 0, []netlink.Attribute{
- {Type: nfQaCfgCmd, Data: []byte{nfUlnlCfgCmdPfBind, 0x0, 0x0, byte(nfqueue.family)}},
- })
- if err != nil {
- return errors.Wrapf(err, "Could not bind to family %d", nfqueue.family)
- }
- // binding to the requested queue
- _, err = nfqueue.setConfig(uint8(unix.AF_UNSPEC), seq, nfqueue.queue, []netlink.Attribute{
- {Type: nfQaCfgCmd, Data: []byte{nfUlnlCfgCmdBind, 0x0, 0x0, byte(nfqueue.family)}},
- })
- if err != nil {
- return errors.Wrapf(err, "Could not bind to requested queue %d", nfqueue.queue)
- }
- // set copy mode and buffer size
- data := append(nfqueue.maxPacketLen, nfqueue.copymode)
- _, err = nfqueue.setConfig(uint8(unix.AF_UNSPEC), seq, nfqueue.queue, []netlink.Attribute{
- {Type: nfQaCfgParams, Data: data},
- })
- if err != nil {
- return err
- }
- var attrs []netlink.Attribute
- if nfqueue.flags[0] != 0 || nfqueue.flags[1] != 0 || nfqueue.flags[2] != 0 || nfqueue.flags[3] != 0 {
- // set flags
- attrs = append(attrs, netlink.Attribute{Type: nfQaCfgFlags, Data: nfqueue.flags})
- attrs = append(attrs, netlink.Attribute{Type: nfQaCfgMask, Data: nfqueue.flags})
- }
- attrs = append(attrs, netlink.Attribute{Type: nfQaCfgQueueMaxLen, Data: nfqueue.maxQueueLen})
- _, err = nfqueue.setConfig(uint8(unix.AF_UNSPEC), seq, nfqueue.queue, attrs)
- if err != nil {
- return err
- }
- go nfqueue.socketCallback(ctx, fn, errfn, seq)
- return nil
- }
- // /include/uapi/linux/netfilter/nfnetlink.h:struct nfgenmsg{} res_id is Big Endian
- func putExtraHeader(familiy, version uint8, resid uint16) []byte {
- buf := make([]byte, 2)
- binary.BigEndian.PutUint16(buf, resid)
- return append([]byte{familiy, version}, buf...)
- }
- func (nfqueue *Nfqueue) setConfig(afFamily uint8, oseq uint32, resid uint16, attrs []netlink.Attribute) (uint32, error) {
- cmd, err := netlink.MarshalAttributes(attrs)
- if err != nil {
- return 0, err
- }
- data := putExtraHeader(afFamily, unix.NFNETLINK_V0, resid)
- data = append(data, cmd...)
- req := netlink.Message{
- Header: netlink.Header{
- Type: netlink.HeaderType((nfnlSubSysQueue << 8) | nfQnlMsgConfig),
- Flags: netlink.Request | netlink.Acknowledge,
- Sequence: oseq,
- },
- Data: data,
- }
- return nfqueue.execute(req)
- }
- func (nfqueue *Nfqueue) execute(req netlink.Message) (uint32, error) {
- var seq uint32
- reply, e := nfqueue.Con.Execute(req)
- if e != nil {
- return 0, e
- }
- if e := netlink.Validate(req, reply); e != nil {
- return 0, e
- }
- for _, msg := range reply {
- if seq != 0 {
- return 0, errors.Wrapf(ErrUnexpMsg, "Number of received messages: %d", len(reply))
- }
- seq = msg.Header.Sequence
- }
- return seq, nil
- }
- func parseMsg(log *log.Logger, msg netlink.Message) (Attribute, error) {
- a, err := extractAttributes(log, msg.Data)
- if err != nil {
- return a, err
- }
- return a, nil
- }
|