From 93a7677a94d648ac415a6b86f07495efd16be9e7 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Fri, 25 Sep 2020 11:04:13 +0800 Subject: [PATCH] ipset: use github.com/nadoo/ipset package --- README.md | 1 + dns/client.go | 8 +- go.mod | 5 +- go.sum | 10 +- ipset/ipset_linux.go | 434 ++----------------------------------------- 5 files changed, 29 insertions(+), 429 deletions(-) diff --git a/README.md b/README.md index fcb670d..12ecbfd 100644 --- a/README.md +++ b/README.md @@ -363,6 +363,7 @@ glider -config CONFIGPATH -listen :8080 -verbose ## Links +- [ipset](https://github.com/nadoo/ipset): ipset package for Go via netlink socket - [conflag](https://github.com/nadoo/conflag): command line and config file parse support - [ArchLinux](https://www.archlinux.org/packages/community/x86_64/glider): a great linux distribution with glider pre-built package - [urlencode](https://www.w3schools.com/tags/ref_urlencode.asp): you should encode special characters in scheme url. e.g: `@`->`%40` diff --git a/dns/client.go b/dns/client.go index 3d306c0..c6150dc 100644 --- a/dns/client.go +++ b/dns/client.go @@ -13,8 +13,8 @@ import ( "github.com/nadoo/glider/proxy" ) -// HandleFunc function handles the dns TypeA or TypeAAAA answer. -type HandleFunc func(domain, ip string) error +// AnswerHandler function handles the dns TypeA or TypeAAAA answer. +type AnswerHandler func(domain, ip string) error // Config for dns. type Config struct { @@ -33,7 +33,7 @@ type Client struct { config *Config upStream *UPStream upStreamMap map[string]*UPStream - handlers []HandleFunc + handlers []AnswerHandler } // NewClient returns a new dns client. @@ -249,7 +249,7 @@ func (c *Client) UpStream(domain string) *UPStream { } // AddHandler adds a custom handler to handle the resolved result (A and AAAA). -func (c *Client) AddHandler(h HandleFunc) { +func (c *Client) AddHandler(h AnswerHandler) { c.handlers = append(c.handlers, h) } diff --git a/go.mod b/go.mod index 6a70e51..78e3d35 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,14 @@ require ( github.com/mzz2017/shadowsocksR v1.0.0 github.com/nadoo/conflag v0.2.3 github.com/nadoo/go-shadowsocks2 v0.1.2 + github.com/nadoo/ipset v0.2.0 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/xtaci/kcp-go/v5 v5.5.15 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/net v0.0.0-20200923182212-328152dc79b1 // indirect golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d // indirect - golang.org/x/tools v0.0.0-20200923182640-463111b69878 // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect + golang.org/x/tools v0.0.0-20200924224222-8d73f17870ce // indirect + gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) // Replace dependency modules with local developing copy diff --git a/go.sum b/go.sum index 7fb260f..f7e0058 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,10 @@ github.com/nadoo/glider v0.9.2/go.mod h1:S/94KRJFNtgoNlyEm4+33f/DrEsj/uxvismOW4F github.com/nadoo/glider v0.10.0/go.mod h1:q5d4Q5yoGk3nLAhshDJalnl0NXJ8Xh4ODEgp+qbdAAg= github.com/nadoo/go-shadowsocks2 v0.1.2 h1:+tCSt65YAAMf24wj3tqv6a9oVBcqSGFYVsifBZwT9w8= github.com/nadoo/go-shadowsocks2 v0.1.2/go.mod h1:/E2kSkS0mqF/e79wcAA0PezoWXk4CY9HldJlzwWtbwU= +github.com/nadoo/ipset v0.1.0 h1:z/rWPoIle8g9PjD/vlsL3h4c++zMRw+Qcq2wjxMZSkU= +github.com/nadoo/ipset v0.1.0/go.mod h1:ugJe3mH5N1UNQbXayGJnLEMALeiwCJYo49Wg4MnZTHU= +github.com/nadoo/ipset v0.2.0 h1:J5dXbW3ntSou3f5vkpnfk6S05u0KvLhzJwGHEiWEtl4= +github.com/nadoo/ipset v0.2.0/go.mod h1:ugJe3mH5N1UNQbXayGJnLEMALeiwCJYo49Wg4MnZTHU= github.com/nadoo/shadowsocksR v0.1.0 h1:sYPxZi0l8F1nxDDcckzb0DHXxhe0LNW5iSeohqPw6Fg= github.com/nadoo/shadowsocksR v0.1.0/go.mod h1:nqcLRU7laARXdLLBrHP8odruT/6GIureicuRTs4R+RY= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= @@ -127,8 +131,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200923182640-463111b69878 h1:VUw1+Jf6KJPf82mbTQMia6HCnNMv2BbAipkEZ4KTcqQ= -golang.org/x/tools v0.0.0-20200923182640-463111b69878/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20200924224222-8d73f17870ce h1:XRr763sMfaUSNR4EsxbddvVEqYFa9picrx6ks9pJkKw= +golang.org/x/tools v0.0.0-20200924224222-8d73f17870ce/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -139,6 +143,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/ipset/ipset_linux.go b/ipset/ipset_linux.go index 1cf1855..99937d9 100644 --- a/ipset/ipset_linux.go +++ b/ipset/ipset_linux.go @@ -1,102 +1,25 @@ -// Apache License 2.0 -// @mdlayher https://github.com/mdlayher/netlink -// Ref: https://github.com/vishvananda/netlink/blob/master/nl/nl_linux.go - package ipset import ( - "bytes" - "encoding/binary" "errors" - "net" "strings" "sync" - "sync/atomic" - "syscall" - "unsafe" - "github.com/nadoo/glider/common/log" + ipsetlib "github.com/nadoo/ipset" + "github.com/nadoo/glider/rule" ) -// NFNL_SUBSYS_IPSET netfilter netlink message types -// https://github.com/torvalds/linux/blob/9e66317d3c92ddaab330c125dfe9d06eee268aff/include/uapi/linux/netfilter/nfnetlink.h#L56 -const NFNL_SUBSYS_IPSET = 6 - -// IPSET_PROTOCOL The protocol version -// http://git.netfilter.org/ipset/tree/include/libipset/linux_ip_set.h -const IPSET_PROTOCOL = 6 - -// IPSET_MAXNAMELEN The max length of strings including NUL: set and type identifiers -const IPSET_MAXNAMELEN = 32 - -// Message types and commands -const ( - IPSET_CMD_CREATE = 2 - IPSET_CMD_FLUSH = 4 - IPSET_CMD_ADD = 9 - IPSET_CMD_DEL = 10 -) - -// Attributes at command level -const ( - IPSET_ATTR_PROTOCOL = 1 /* 1: Protocol version */ - IPSET_ATTR_SETNAME = 2 /* 2: Name of the set */ - IPSET_ATTR_TYPENAME = 3 /* 3: Typename */ - IPSET_ATTR_REVISION = 4 /* 4: Settype revision */ - IPSET_ATTR_FAMILY = 5 /* 5: Settype family */ - IPSET_ATTR_DATA = 7 /* 7: Nested attributes */ -) - -// CADT specific attributes -const ( - IPSET_ATTR_IP = 1 - IPSET_ATTR_CIDR = 3 -) - -// IP specific attributes -const ( - IPSET_ATTR_IPADDR_IPV4 = 1 - IPSET_ATTR_IPADDR_IPV6 = 2 -) - -// ATTR flags -const ( - NLA_F_NESTED = (1 << 15) - NLA_F_NET_BYTEORDER = (1 << 14) -) - -var nextSeqNr uint32 -var nativeEndian binary.ByteOrder - -// Manager struct +// Manager struct. type Manager struct { - fd int - lsa syscall.SockaddrNetlink - domainSet sync.Map } // NewManager returns a Manager func NewManager(rules []*rule.Config) (*Manager, error) { - fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_NETFILTER) - if err != nil { - log.F("%s", err) + if err := ipsetlib.Init(); err != nil { return nil, err } - // defer syscall.Close(fd) - - lsa := syscall.SockaddrNetlink{ - Family: syscall.AF_NETLINK, - } - - err = syscall.Bind(fd, &lsa) - if err != nil { - log.F("%s", err) - return nil, err - } - - m := &Manager{fd: fd, lsa: lsa} // create ipset, avoid redundant. sets := make(map[string]struct{}) @@ -107,20 +30,22 @@ func NewManager(rules []*rule.Config) (*Manager, error) { } for set := range sets { - CreateSet(fd, lsa, set) + ipsetlib.Create(set) + ipsetlib.Flush(set) } // init ipset + m := &Manager{} for _, r := range rules { if r.IPSet != "" { for _, domain := range r.Domain { m.domainSet.Store(domain, r.IPSet) } for _, ip := range r.IP { - AddToSet(fd, lsa, r.IPSet, ip) + ipsetlib.Add(r.IPSet, ip) } for _, cidr := range r.CIDR { - AddToSet(fd, lsa, r.IPSet, cidr) + ipsetlib.Add(r.IPSet, cidr) } } } @@ -128,352 +53,19 @@ func NewManager(rules []*rule.Config) (*Manager, error) { return m, nil } -// AddDomainIP implements the DNSAnswerHandler function, used to update ipset according to domainSet rule +// AddDomainIP implements the dns AnswerHandler function, used to update ipset according to domainSet rule. func (m *Manager) AddDomainIP(domain, ip string) error { if domain == "" || ip == "" { return errors.New("please specify the domain and ip address") } + domain = strings.ToLower(domain) for i := len(domain); i != -1; { i = strings.LastIndexByte(domain[:i], '.') if ipset, ok := m.domainSet.Load(domain[i+1:]); ok { - AddToSet(m.fd, m.lsa, ipset.(string), ip) + ipsetlib.Add(ipset.(string), ip) } } + return nil } - -// CreateSet create a ipset -func CreateSet(fd int, lsa syscall.SockaddrNetlink, setName string) { - if setName == "" { - return - } - - if len(setName) > IPSET_MAXNAMELEN { - log.Fatal("ipset: name too long") - } - - log.F("ipset create %s hash:net", setName) - - req := NewNetlinkRequest(IPSET_CMD_CREATE|(NFNL_SUBSYS_IPSET<<8), syscall.NLM_F_REQUEST) - - // TODO: support AF_INET6 - req.AddData(NewNfGenMsg(syscall.AF_INET, 0, 0)) - req.AddData(NewRtAttr(IPSET_ATTR_PROTOCOL, Uint8Attr(IPSET_PROTOCOL))) - req.AddData(NewRtAttr(IPSET_ATTR_SETNAME, ZeroTerminated(setName))) - req.AddData(NewRtAttr(IPSET_ATTR_TYPENAME, ZeroTerminated("hash:net"))) - req.AddData(NewRtAttr(IPSET_ATTR_REVISION, Uint8Attr(1))) - req.AddData(NewRtAttr(IPSET_ATTR_FAMILY, Uint8Attr(2))) - req.AddData(NewRtAttr(IPSET_ATTR_DATA|NLA_F_NESTED, nil)) - - err := syscall.Sendto(fd, req.Serialize(), 0, &lsa) - if err != nil { - log.F("%s", err) - } - - FlushSet(fd, lsa, setName) -} - -// FlushSet flush a ipset -func FlushSet(fd int, lsa syscall.SockaddrNetlink, setName string) { - log.F("ipset flush %s", setName) - - req := NewNetlinkRequest(IPSET_CMD_FLUSH|(NFNL_SUBSYS_IPSET<<8), syscall.NLM_F_REQUEST) - - // TODO: support AF_INET6 - req.AddData(NewNfGenMsg(syscall.AF_INET, 0, 0)) - req.AddData(NewRtAttr(IPSET_ATTR_PROTOCOL, Uint8Attr(IPSET_PROTOCOL))) - req.AddData(NewRtAttr(IPSET_ATTR_SETNAME, ZeroTerminated(setName))) - - err := syscall.Sendto(fd, req.Serialize(), 0, &lsa) - if err != nil { - log.F("%s", err) - } - -} - -// AddToSet adds an entry to ipset -func AddToSet(fd int, lsa syscall.SockaddrNetlink, setName, entry string) { - if setName == "" { - return - } - - if len(setName) > IPSET_MAXNAMELEN { - log.F("ipset: name too long") - } - - log.F("ipset add %s %s", setName, entry) - - var ip net.IP - var cidr *net.IPNet - - ip, cidr, err := net.ParseCIDR(entry) - if err != nil { - ip = net.ParseIP(entry) - } - - if ip == nil { - log.F("ipset: parse %s error", entry) - return - } - - req := NewNetlinkRequest(IPSET_CMD_ADD|(NFNL_SUBSYS_IPSET<<8), syscall.NLM_F_REQUEST) - - // TODO: support AF_INET6 - req.AddData(NewNfGenMsg(syscall.AF_INET, 0, 0)) - req.AddData(NewRtAttr(IPSET_ATTR_PROTOCOL, Uint8Attr(IPSET_PROTOCOL))) - req.AddData(NewRtAttr(IPSET_ATTR_SETNAME, ZeroTerminated(setName))) - - attrNested := NewRtAttr(IPSET_ATTR_DATA|NLA_F_NESTED, nil) - attrIP := NewRtAttrChild(attrNested, IPSET_ATTR_IP|NLA_F_NESTED, nil) - - // TODO: support ipV6 - NewRtAttrChild(attrIP, IPSET_ATTR_IPADDR_IPV4|NLA_F_NET_BYTEORDER, ip.To4()) - - // for cidr prefix - if cidr != nil { - cidrPrefix, _ := cidr.Mask.Size() - NewRtAttrChild(attrNested, IPSET_ATTR_CIDR, Uint8Attr(uint8(cidrPrefix))) - } - - NewRtAttrChild(attrNested, 9|NLA_F_NET_BYTEORDER, Uint32Attr(0)) - req.AddData(attrNested) - - err = syscall.Sendto(fd, req.Serialize(), 0, &lsa) - if err != nil { - log.F("%s", err) - } -} - -// NativeEndian get native endianness for the system -func NativeEndian() binary.ByteOrder { - if nativeEndian == nil { - var x uint32 = 0x01020304 - if *(*byte)(unsafe.Pointer(&x)) == 0x01 { - nativeEndian = binary.BigEndian - } else { - nativeEndian = binary.LittleEndian - } - } - return nativeEndian -} - -func rtaAlignOf(attrlen int) int { - return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1) -} - -// NetlinkRequestData . -type NetlinkRequestData interface { - Len() int - Serialize() []byte -} - -// NfGenMsg . -type NfGenMsg struct { - nfgenFamily uint8 - version uint8 - resID uint16 -} - -// NewNfGenMsg . -func NewNfGenMsg(nfgenFamily, version, resID int) *NfGenMsg { - return &NfGenMsg{ - nfgenFamily: uint8(nfgenFamily), - version: uint8(version), - resID: uint16(resID), - } -} - -// Len . -func (m *NfGenMsg) Len() int { - return rtaAlignOf(4) -} - -// Serialize . -func (m *NfGenMsg) Serialize() []byte { - native := NativeEndian() - - length := m.Len() - buf := make([]byte, rtaAlignOf(length)) - buf[0] = m.nfgenFamily - buf[1] = m.version - native.PutUint16(buf[2:4], m.resID) - return buf -} - -// RtAttr Extend RtAttr to handle data and children -type RtAttr struct { - syscall.RtAttr - Data []byte - children []NetlinkRequestData -} - -// NewRtAttr Create a new Extended RtAttr object -func NewRtAttr(attrType int, data []byte) *RtAttr { - return &RtAttr{ - RtAttr: syscall.RtAttr{ - Type: uint16(attrType), - }, - children: []NetlinkRequestData{}, - Data: data, - } -} - -// NewRtAttrChild Create a new RtAttr obj anc add it as a child of an existing object -func NewRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr { - attr := NewRtAttr(attrType, data) - parent.children = append(parent.children, attr) - return attr -} - -// Len . -func (a *RtAttr) Len() int { - if len(a.children) == 0 { - return (syscall.SizeofRtAttr + len(a.Data)) - } - - l := 0 - for _, child := range a.children { - l += rtaAlignOf(child.Len()) - } - l += syscall.SizeofRtAttr - return rtaAlignOf(l + len(a.Data)) -} - -// Serialize the RtAttr into a byte array -// This can't just unsafe.cast because it must iterate through children. -func (a *RtAttr) Serialize() []byte { - native := NativeEndian() - - length := a.Len() - buf := make([]byte, rtaAlignOf(length)) - - next := 4 - if a.Data != nil { - copy(buf[next:], a.Data) - next += rtaAlignOf(len(a.Data)) - } - if len(a.children) > 0 { - for _, child := range a.children { - childBuf := child.Serialize() - copy(buf[next:], childBuf) - next += rtaAlignOf(len(childBuf)) - } - } - - if l := uint16(length); l != 0 { - native.PutUint16(buf[0:2], l) - } - native.PutUint16(buf[2:4], a.Type) - return buf -} - -// NetlinkRequest . -type NetlinkRequest struct { - syscall.NlMsghdr - Data []NetlinkRequestData - RawData []byte -} - -// NewNetlinkRequest create a new netlink request from proto and flags -// Note the Len value will be inaccurate once data is added until -// the message is serialized -func NewNetlinkRequest(proto, flags int) *NetlinkRequest { - return &NetlinkRequest{ - NlMsghdr: syscall.NlMsghdr{ - Len: uint32(syscall.SizeofNlMsghdr), - Type: uint16(proto), - Flags: syscall.NLM_F_REQUEST | uint16(flags), - Seq: atomic.AddUint32(&nextSeqNr, 1), - // Pid: uint32(os.Getpid()), - }, - } -} - -// Serialize the Netlink Request into a byte array -func (req *NetlinkRequest) Serialize() []byte { - length := syscall.SizeofNlMsghdr - dataBytes := make([][]byte, len(req.Data)) - for i, data := range req.Data { - dataBytes[i] = data.Serialize() - length = length + len(dataBytes[i]) - } - length += len(req.RawData) - - req.Len = uint32(length) - b := make([]byte, length) - hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:] - next := syscall.SizeofNlMsghdr - copy(b[0:next], hdr) - for _, data := range dataBytes { - for _, dataByte := range data { - b[next] = dataByte - next = next + 1 - } - } - // Add the raw data if any - if len(req.RawData) > 0 { - copy(b[next:length], req.RawData) - } - return b -} - -// AddData add data to request -func (req *NetlinkRequest) AddData(data NetlinkRequestData) { - if data != nil { - req.Data = append(req.Data, data) - } -} - -// AddRawData adds raw bytes to the end of the NetlinkRequest object during serialization -func (req *NetlinkRequest) AddRawData(data []byte) { - if data != nil { - req.RawData = append(req.RawData, data...) - } -} - -// Uint8Attr . -func Uint8Attr(v uint8) []byte { - return []byte{byte(v)} -} - -// Uint16Attr . -func Uint16Attr(v uint16) []byte { - native := NativeEndian() - bytes := make([]byte, 2) - native.PutUint16(bytes, v) - return bytes -} - -// Uint32Attr . -func Uint32Attr(v uint32) []byte { - native := NativeEndian() - bytes := make([]byte, 4) - native.PutUint32(bytes, v) - return bytes -} - -// ZeroTerminated . -func ZeroTerminated(s string) []byte { - bytes := make([]byte, len(s)+1) - for i := 0; i < len(s); i++ { - bytes[i] = s[i] - } - bytes[len(s)] = 0 - return bytes -} - -// NonZeroTerminated . -func NonZeroTerminated(s string) []byte { - bytes := make([]byte, len(s)) - for i := 0; i < len(s); i++ { - bytes[i] = s[i] - } - return bytes -} - -// BytesToString . -func BytesToString(b []byte) string { - n := bytes.Index(b, []byte{0}) - return string(b[:n]) -}