istio.io/istio@v0.0.0-20240520182934-d79c90f27776/cni/pkg/ipset/ipset.go (about) 1 // Copyright Istio 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 15 package ipset 16 17 import ( 18 "errors" 19 "fmt" 20 "net/netip" 21 ) 22 23 type IPSet struct { 24 V4Name string 25 V6Name string 26 Prefix string 27 Deps NetlinkIpsetDeps 28 } 29 30 const ( 31 V4Name = "%s-v4" 32 V6Name = "%s-v6" 33 ) 34 35 type NetlinkIpsetDeps interface { 36 ipsetIPHashCreate(name string, v6 bool) error 37 destroySet(name string) error 38 addIP(name string, ip netip.Addr, ipProto uint8, comment string, replace bool) error 39 deleteIP(name string, ip netip.Addr, ipProto uint8) error 40 flush(name string) error 41 clearEntriesWithComment(name string, comment string) error 42 clearEntriesWithIP(name string, ip netip.Addr) error 43 listEntriesByIP(name string) ([]netip.Addr, error) 44 } 45 46 // TODO this should actually create v6 and v6 subsets of type `hash:ip`, add them both to a 47 // superset of type `list:set` - we can then query the superset directly in iptables (with the same rule), 48 // and iptables will be smart enough to pick the correct underlying set (v4 or v6, based on context), 49 // reducing the # of rules we need. 50 // 51 // BUT netlink lib doesn't support adding things to `list:set` types yet, and current tagged release 52 // doesn't support creating `list:set` types yet (is in main branch tho). 53 // So this will actually create 2 underlying ipsets, one for v4 and one for v6 54 func NewIPSet(name string, v6 bool, deps NetlinkIpsetDeps) (IPSet, error) { 55 var err error 56 set := IPSet{ 57 V4Name: fmt.Sprintf(V4Name, name), 58 Deps: deps, 59 Prefix: name, 60 } 61 err = deps.ipsetIPHashCreate(set.V4Name, false) 62 if v6 { 63 set.V6Name = fmt.Sprintf(V6Name, name) 64 v6err := deps.ipsetIPHashCreate(set.V6Name, true) 65 err = errors.Join(err, v6err) 66 } 67 return set, err 68 } 69 70 func (m *IPSet) DestroySet() error { 71 var err error 72 err = m.Deps.destroySet(m.V4Name) 73 74 if m.V6Name != "" { 75 v6err := m.Deps.destroySet(m.V6Name) 76 err = errors.Join(err, v6err) 77 } 78 return err 79 } 80 81 func (m *IPSet) AddIP(ip netip.Addr, ipProto uint8, comment string, replace bool) error { 82 ipToInsert := ip.Unmap() 83 84 // We have already Unmap'd, so we can do a simple IsV6 y/n check now 85 if ipToInsert.Is6() { 86 return m.Deps.addIP(m.V6Name, ipToInsert, ipProto, comment, replace) 87 } 88 return m.Deps.addIP(m.V4Name, ipToInsert, ipProto, comment, replace) 89 } 90 91 func (m *IPSet) DeleteIP(ip netip.Addr, ipProto uint8) error { 92 ipToDel := ip.Unmap() 93 94 // We have already Unmap'd, so we can do a simple IsV6 y/n check now 95 if ipToDel.Is6() { 96 return m.Deps.deleteIP(m.V6Name, ipToDel, ipProto) 97 } 98 return m.Deps.deleteIP(m.V4Name, ipToDel, ipProto) 99 } 100 101 func (m *IPSet) Flush() error { 102 var err error 103 err = m.Deps.flush(m.V4Name) 104 105 if m.V6Name != "" { 106 v6err := m.Deps.flush(m.V6Name) 107 err = errors.Join(err, v6err) 108 } 109 return err 110 } 111 112 func (m *IPSet) ClearEntriesWithComment(comment string) error { 113 var err error 114 err = m.Deps.clearEntriesWithComment(m.V4Name, comment) 115 116 if m.V6Name != "" { 117 v6err := m.Deps.clearEntriesWithComment(m.V6Name, comment) 118 err = errors.Join(err, v6err) 119 } 120 return err 121 } 122 123 func (m *IPSet) ClearEntriesWithIP(ip netip.Addr) error { 124 ipToClear := ip.Unmap() 125 126 if ipToClear.Is6() { 127 return m.Deps.clearEntriesWithIP(m.V6Name, ipToClear) 128 } 129 return m.Deps.clearEntriesWithIP(m.V4Name, ipToClear) 130 } 131 132 func (m *IPSet) ListEntriesByIP() ([]netip.Addr, error) { 133 var err error 134 var set []netip.Addr 135 set, err = m.Deps.listEntriesByIP(m.V4Name) 136 137 if m.V6Name != "" { 138 v6set, v6err := m.Deps.listEntriesByIP(m.V6Name) 139 err = errors.Join(err, v6err) 140 set = append(set, v6set...) 141 } 142 return set, err 143 }