chain.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // Copyright 2018 Google LLC. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package nftables
  15. import (
  16. "encoding/binary"
  17. "fmt"
  18. "math"
  19. "github.com/google/nftables/binaryutil"
  20. "github.com/mdlayher/netlink"
  21. "golang.org/x/sys/unix"
  22. )
  23. // ChainHook specifies at which step in packet processing the Chain should be
  24. // executed. See also
  25. // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_hooks
  26. type ChainHook uint32
  27. // Possible ChainHook values.
  28. var (
  29. ChainHookPrerouting *ChainHook = ChainHookRef(unix.NF_INET_PRE_ROUTING)
  30. ChainHookInput *ChainHook = ChainHookRef(unix.NF_INET_LOCAL_IN)
  31. ChainHookForward *ChainHook = ChainHookRef(unix.NF_INET_FORWARD)
  32. ChainHookOutput *ChainHook = ChainHookRef(unix.NF_INET_LOCAL_OUT)
  33. ChainHookPostrouting *ChainHook = ChainHookRef(unix.NF_INET_POST_ROUTING)
  34. ChainHookIngress *ChainHook = ChainHookRef(unix.NF_NETDEV_INGRESS)
  35. )
  36. // ChainHookRef returns a pointer to a ChainHookRef value.
  37. func ChainHookRef(h ChainHook) *ChainHook {
  38. return &h
  39. }
  40. // ChainPriority orders the chain relative to Netfilter internal operations. See
  41. // also
  42. // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_priority
  43. type ChainPriority int32
  44. // Possible ChainPriority values.
  45. var ( // from /usr/include/linux/netfilter_ipv4.h
  46. ChainPriorityFirst *ChainPriority = ChainPriorityRef(math.MinInt32)
  47. ChainPriorityConntrackDefrag *ChainPriority = ChainPriorityRef(-400)
  48. ChainPriorityRaw *ChainPriority = ChainPriorityRef(-300)
  49. ChainPrioritySELinuxFirst *ChainPriority = ChainPriorityRef(-225)
  50. ChainPriorityConntrack *ChainPriority = ChainPriorityRef(-200)
  51. ChainPriorityMangle *ChainPriority = ChainPriorityRef(-150)
  52. ChainPriorityNATDest *ChainPriority = ChainPriorityRef(-100)
  53. ChainPriorityFilter *ChainPriority = ChainPriorityRef(0)
  54. ChainPrioritySecurity *ChainPriority = ChainPriorityRef(50)
  55. ChainPriorityNATSource *ChainPriority = ChainPriorityRef(100)
  56. ChainPrioritySELinuxLast *ChainPriority = ChainPriorityRef(225)
  57. ChainPriorityConntrackHelper *ChainPriority = ChainPriorityRef(300)
  58. ChainPriorityConntrackConfirm *ChainPriority = ChainPriorityRef(math.MaxInt32)
  59. ChainPriorityLast *ChainPriority = ChainPriorityRef(math.MaxInt32)
  60. )
  61. // ChainPriorityRef returns a pointer to a ChainPriority value.
  62. func ChainPriorityRef(p ChainPriority) *ChainPriority {
  63. return &p
  64. }
  65. // ChainType defines what this chain will be used for. See also
  66. // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_types
  67. type ChainType string
  68. // Possible ChainType values.
  69. const (
  70. ChainTypeFilter ChainType = "filter"
  71. ChainTypeRoute ChainType = "route"
  72. ChainTypeNAT ChainType = "nat"
  73. )
  74. // ChainPolicy defines what this chain default policy will be.
  75. type ChainPolicy uint32
  76. // Possible ChainPolicy values.
  77. const (
  78. ChainPolicyDrop ChainPolicy = iota
  79. ChainPolicyAccept
  80. )
  81. // A Chain contains Rules. See also
  82. // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains
  83. type Chain struct {
  84. Name string
  85. Table *Table
  86. Hooknum *ChainHook
  87. Priority *ChainPriority
  88. Type ChainType
  89. Policy *ChainPolicy
  90. }
  91. // AddChain adds the specified Chain. See also
  92. // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Adding_base_chains
  93. func (cc *Conn) AddChain(c *Chain) *Chain {
  94. cc.mu.Lock()
  95. defer cc.mu.Unlock()
  96. data := cc.marshalAttr([]netlink.Attribute{
  97. {Type: unix.NFTA_CHAIN_TABLE, Data: []byte(c.Table.Name + "\x00")},
  98. {Type: unix.NFTA_CHAIN_NAME, Data: []byte(c.Name + "\x00")},
  99. })
  100. if c.Hooknum != nil && c.Priority != nil {
  101. hookAttr := []netlink.Attribute{
  102. {Type: unix.NFTA_HOOK_HOOKNUM, Data: binaryutil.BigEndian.PutUint32(uint32(*c.Hooknum))},
  103. {Type: unix.NFTA_HOOK_PRIORITY, Data: binaryutil.BigEndian.PutUint32(uint32(*c.Priority))},
  104. }
  105. data = append(data, cc.marshalAttr([]netlink.Attribute{
  106. {Type: unix.NLA_F_NESTED | unix.NFTA_CHAIN_HOOK, Data: cc.marshalAttr(hookAttr)},
  107. })...)
  108. }
  109. if c.Policy != nil {
  110. data = append(data, cc.marshalAttr([]netlink.Attribute{
  111. {Type: unix.NFTA_CHAIN_POLICY, Data: binaryutil.BigEndian.PutUint32(uint32(*c.Policy))},
  112. })...)
  113. }
  114. if c.Type != "" {
  115. data = append(data, cc.marshalAttr([]netlink.Attribute{
  116. {Type: unix.NFTA_CHAIN_TYPE, Data: []byte(c.Type + "\x00")},
  117. })...)
  118. }
  119. cc.messages = append(cc.messages, netlink.Message{
  120. Header: netlink.Header{
  121. Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWCHAIN),
  122. Flags: netlink.Request | netlink.Acknowledge | netlink.Create,
  123. },
  124. Data: append(extraHeader(uint8(c.Table.Family), 0), data...),
  125. })
  126. return c
  127. }
  128. // DelChain deletes the specified Chain. See also
  129. // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Deleting_chains
  130. func (cc *Conn) DelChain(c *Chain) {
  131. cc.mu.Lock()
  132. defer cc.mu.Unlock()
  133. data := cc.marshalAttr([]netlink.Attribute{
  134. {Type: unix.NFTA_CHAIN_TABLE, Data: []byte(c.Table.Name + "\x00")},
  135. {Type: unix.NFTA_CHAIN_NAME, Data: []byte(c.Name + "\x00")},
  136. })
  137. cc.messages = append(cc.messages, netlink.Message{
  138. Header: netlink.Header{
  139. Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELCHAIN),
  140. Flags: netlink.Request | netlink.Acknowledge,
  141. },
  142. Data: append(extraHeader(uint8(c.Table.Family), 0), data...),
  143. })
  144. }
  145. // FlushChain removes all rules within the specified Chain. See also
  146. // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Flushing_chain
  147. func (cc *Conn) FlushChain(c *Chain) {
  148. cc.mu.Lock()
  149. defer cc.mu.Unlock()
  150. data := cc.marshalAttr([]netlink.Attribute{
  151. {Type: unix.NFTA_RULE_TABLE, Data: []byte(c.Table.Name + "\x00")},
  152. {Type: unix.NFTA_RULE_CHAIN, Data: []byte(c.Name + "\x00")},
  153. })
  154. cc.messages = append(cc.messages, netlink.Message{
  155. Header: netlink.Header{
  156. Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_DELRULE),
  157. Flags: netlink.Request | netlink.Acknowledge,
  158. },
  159. Data: append(extraHeader(uint8(c.Table.Family), 0), data...),
  160. })
  161. }
  162. // ListChains returns currently configured chains in the kernel
  163. func (cc *Conn) ListChains() ([]*Chain, error) {
  164. return cc.ListChainsOfTableFamily(TableFamilyUnspecified)
  165. }
  166. // ListChainsOfTableFamily returns currently configured chains for the specified
  167. // family in the kernel. It lists all chains ins all tables if family is
  168. // TableFamilyUnspecified.
  169. func (cc *Conn) ListChainsOfTableFamily(family TableFamily) ([]*Chain, error) {
  170. conn, closer, err := cc.netlinkConn()
  171. if err != nil {
  172. return nil, err
  173. }
  174. defer func() { _ = closer() }()
  175. msg := netlink.Message{
  176. Header: netlink.Header{
  177. Type: netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_GETCHAIN),
  178. Flags: netlink.Request | netlink.Dump,
  179. },
  180. Data: extraHeader(uint8(family), 0),
  181. }
  182. response, err := conn.Execute(msg)
  183. if err != nil {
  184. return nil, err
  185. }
  186. var chains []*Chain
  187. for _, m := range response {
  188. c, err := chainFromMsg(m)
  189. if err != nil {
  190. return nil, err
  191. }
  192. chains = append(chains, c)
  193. }
  194. return chains, nil
  195. }
  196. func chainFromMsg(msg netlink.Message) (*Chain, error) {
  197. chainHeaderType := netlink.HeaderType((unix.NFNL_SUBSYS_NFTABLES << 8) | unix.NFT_MSG_NEWCHAIN)
  198. if got, want := msg.Header.Type, chainHeaderType; got != want {
  199. return nil, fmt.Errorf("unexpected header type: got %v, want %v", got, want)
  200. }
  201. var c Chain
  202. ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
  203. if err != nil {
  204. return nil, err
  205. }
  206. for ad.Next() {
  207. switch ad.Type() {
  208. case unix.NFTA_CHAIN_NAME:
  209. c.Name = ad.String()
  210. case unix.NFTA_TABLE_NAME:
  211. c.Table = &Table{Name: ad.String()}
  212. // msg[0] carries TableFamily byte indicating whether it is IPv4, IPv6 or something else
  213. c.Table.Family = TableFamily(msg.Data[0])
  214. case unix.NFTA_CHAIN_TYPE:
  215. c.Type = ChainType(ad.String())
  216. case unix.NFTA_CHAIN_POLICY:
  217. policy := ChainPolicy(binaryutil.BigEndian.Uint32(ad.Bytes()))
  218. c.Policy = &policy
  219. case unix.NFTA_CHAIN_HOOK:
  220. ad.Do(func(b []byte) error {
  221. c.Hooknum, c.Priority, err = hookFromMsg(b)
  222. return err
  223. })
  224. }
  225. }
  226. return &c, nil
  227. }
  228. func hookFromMsg(b []byte) (*ChainHook, *ChainPriority, error) {
  229. ad, err := netlink.NewAttributeDecoder(b)
  230. if err != nil {
  231. return nil, nil, err
  232. }
  233. ad.ByteOrder = binary.BigEndian
  234. var hooknum ChainHook
  235. var prio ChainPriority
  236. for ad.Next() {
  237. switch ad.Type() {
  238. case unix.NFTA_HOOK_HOOKNUM:
  239. hooknum = ChainHook(ad.Uint32())
  240. case unix.NFTA_HOOK_PRIORITY:
  241. prio = ChainPriority(ad.Uint32())
  242. }
  243. }
  244. return &hooknum, &prio, nil
  245. }