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  }