2
0

rulecache.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // Copyright 2021 the Kilo authors
  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 iptables
  15. import (
  16. "fmt"
  17. "strings"
  18. )
  19. type ruleCacheFlag byte
  20. const (
  21. exists ruleCacheFlag = 1 << iota
  22. populated
  23. )
  24. type isNotExistError interface {
  25. error
  26. IsNotExist() bool
  27. }
  28. // ruleCache is a lazy cache that can be used to
  29. // check if a given rule or chain exists in an iptables
  30. // table.
  31. type ruleCache [2]map[string]ruleCacheFlag
  32. func (rc *ruleCache) populateTable(c Client, proto Protocol, table string) error {
  33. // If the table already exists in the destination map,
  34. // exit early since it has already been populated.
  35. if rc[proto][table]&populated != 0 {
  36. return nil
  37. }
  38. cs, err := c.ListChains(table)
  39. if err != nil {
  40. return fmt.Errorf("failed to populate chains for table %q: %v", table, err)
  41. }
  42. rc[proto][table] = exists | populated
  43. for i := range cs {
  44. rc[proto][chainToString(table, cs[i])] |= exists
  45. }
  46. return nil
  47. }
  48. func (rc *ruleCache) populateChain(c Client, proto Protocol, table, chain string) error {
  49. // If the destination chain true, then it has already been populated.
  50. if rc[proto][chainToString(table, chain)]&populated != 0 {
  51. return nil
  52. }
  53. rs, err := c.List(table, chain)
  54. if err != nil {
  55. if existsErr, ok := err.(isNotExistError); ok && existsErr.IsNotExist() {
  56. rc[proto][chainToString(table, chain)] = populated
  57. return nil
  58. }
  59. return fmt.Errorf("failed to populate rules in chain %q for table %q: %v", chain, table, err)
  60. }
  61. for i := range rs {
  62. rc[proto][strings.Join([]string{table, rs[i]}, " ")] = exists
  63. }
  64. // If there are rules on the chain, then the chain exists too.
  65. if len(rs) > 0 {
  66. rc[proto][chainToString(table, chain)] = exists
  67. }
  68. rc[proto][chainToString(table, chain)] |= populated
  69. return nil
  70. }
  71. func (rc *ruleCache) populateRules(c Client, r Rule) error {
  72. // Ensure a map for the proto exists.
  73. if rc[r.Proto()] == nil {
  74. rc[r.Proto()] = make(map[string]ruleCacheFlag)
  75. }
  76. if ch, ok := r.(*chain); ok {
  77. return rc.populateTable(c, r.Proto(), ch.table)
  78. }
  79. ru := r.(*rule)
  80. return rc.populateChain(c, r.Proto(), ru.table, ru.chain)
  81. }
  82. func (rc *ruleCache) exists(c Client, r Rule) (bool, error) {
  83. // Exit early if the exact rule exists by name.
  84. if rc[r.Proto()][r.String()]&exists != 0 {
  85. return true, nil
  86. }
  87. // Otherwise, populate the respective rules.
  88. if err := rc.populateRules(c, r); err != nil {
  89. return false, err
  90. }
  91. return rc[r.Proto()][r.String()]&exists != 0, nil
  92. }