github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/maps/nat/nat.go (about)

     1  // Copyright 2019 Authors of Cilium
     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 nat
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"unsafe"
    21  
    22  	"github.com/cilium/cilium/pkg/bpf"
    23  	"github.com/cilium/cilium/pkg/logging"
    24  	"github.com/cilium/cilium/pkg/logging/logfields"
    25  	"github.com/cilium/cilium/pkg/option"
    26  	"github.com/cilium/cilium/pkg/tuple"
    27  )
    28  
    29  var (
    30  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "map-nat")
    31  )
    32  
    33  const (
    34  	// MapNameSnat4Global represents global IPv4 NAT table.
    35  	MapNameSnat4Global = "cilium_snat_v4_external"
    36  	// MapNameSnat6Global represents global IPv6 NAT table.
    37  	MapNameSnat6Global = "cilium_snat_v6_external"
    38  
    39  	// MinPortSnatDefault represents default min port from range.
    40  	MinPortSnatDefault = 1024
    41  	// MaxPortSnatDefault represents default max port from range.
    42  	MaxPortSnatDefault = 65535
    43  
    44  	mapCount = 2
    45  )
    46  
    47  // Map represents a NAT map.
    48  type Map struct {
    49  	bpf.Map
    50  	v4 bool
    51  }
    52  
    53  // NatEntry is the interface describing values to the NAT map.
    54  type NatEntry interface {
    55  	bpf.MapValue
    56  
    57  	// ToHost converts fields to host byte order.
    58  	ToHost() NatEntry
    59  
    60  	// Dumps the Nat entry as string.
    61  	Dump(key NatKey, start uint64) string
    62  }
    63  
    64  // NatDumpCreated returns time in seconds when NAT entry was created.
    65  func NatDumpCreated(dumpStart, entryCreated uint64) string {
    66  	tsecCreated := entryCreated / 1000000000
    67  	tsecStart := dumpStart / 1000000000
    68  
    69  	return fmt.Sprintf("%dsec", tsecStart-tsecCreated)
    70  }
    71  
    72  // NewMap instantiates a Map.
    73  func NewMap(name string, v4 bool, entries int) *Map {
    74  	var sizeKey, sizeVal int
    75  	var mapKey bpf.MapKey
    76  	var mapValue bpf.MapValue
    77  
    78  	if v4 {
    79  		mapKey = &NatKey4{}
    80  		sizeKey = int(unsafe.Sizeof(NatKey4{}))
    81  		mapValue = &NatEntry4{}
    82  		sizeVal = int(unsafe.Sizeof(NatEntry4{}))
    83  	} else {
    84  		mapKey = &NatKey6{}
    85  		sizeKey = int(unsafe.Sizeof(NatKey6{}))
    86  		mapValue = &NatEntry6{}
    87  		sizeVal = int(unsafe.Sizeof(NatEntry6{}))
    88  	}
    89  	return &Map{
    90  		Map: *bpf.NewMap(
    91  			name,
    92  			bpf.MapTypeLRUHash,
    93  			mapKey,
    94  			sizeKey,
    95  			mapValue,
    96  			sizeVal,
    97  			entries,
    98  			0, 0,
    99  			bpf.ConvertKeyValue,
   100  		).WithCache(),
   101  		v4: v4,
   102  	}
   103  }
   104  
   105  // DumpEntries iterates through Map m and writes the values of the
   106  // nat entries in m to a string.
   107  func (m *Map) DumpEntries() (string, error) {
   108  	var buffer bytes.Buffer
   109  
   110  	nsecStart, _ := bpf.GetMtime()
   111  	cb := func(k bpf.MapKey, v bpf.MapValue) {
   112  		key := k.(NatKey)
   113  		if !key.ToHost().Dump(&buffer, false) {
   114  			return
   115  		}
   116  		val := v.(NatEntry)
   117  		buffer.WriteString(val.ToHost().Dump(key, nsecStart))
   118  	}
   119  	err := m.DumpWithCallback(cb)
   120  	return buffer.String(), err
   121  }
   122  
   123  type gcStats struct {
   124  	*bpf.DumpStats
   125  
   126  	// deleted is the number of keys deleted
   127  	deleted uint32
   128  
   129  	// dumpError records any error that occurred during the dump.
   130  	dumpError error
   131  }
   132  
   133  func statStartGc(m *Map) gcStats {
   134  	return gcStats{
   135  		DumpStats: bpf.NewDumpStats(&m.Map),
   136  	}
   137  }
   138  
   139  func doFlush4(m *Map) gcStats {
   140  	stats := statStartGc(m)
   141  	filterCallback := func(key bpf.MapKey, _ bpf.MapValue) {
   142  		err := m.Delete(key)
   143  		if err != nil {
   144  			log.WithError(err).WithField(logfields.Key, key.String()).Error("Unable to delete CT entry")
   145  		} else {
   146  			stats.deleted++
   147  		}
   148  	}
   149  	stats.dumpError = m.DumpReliablyWithCallback(filterCallback, stats.DumpStats)
   150  	return stats
   151  }
   152  
   153  func doFlush6(m *Map) gcStats {
   154  	stats := statStartGc(m)
   155  	filterCallback := func(key bpf.MapKey, _ bpf.MapValue) {
   156  		err := m.Delete(key)
   157  		if err != nil {
   158  			log.WithError(err).WithField(logfields.Key, key.String()).Error("Unable to delete CT entry")
   159  		} else {
   160  			stats.deleted++
   161  		}
   162  	}
   163  	stats.dumpError = m.DumpReliablyWithCallback(filterCallback, stats.DumpStats)
   164  	return stats
   165  }
   166  
   167  // Flush deletes all NAT mappings from the given table.
   168  func (m *Map) Flush() int {
   169  	if m.v4 {
   170  		return int(doFlush4(m).deleted)
   171  	}
   172  	return int(doFlush6(m).deleted)
   173  }
   174  
   175  func deleteMapping4(m *Map, ctKey *tuple.TupleKey4Global) error {
   176  	key := NatKey4{
   177  		TupleKey4Global: *ctKey,
   178  	}
   179  	// Workaround #5848.
   180  	addr := key.SourceAddr
   181  	key.SourceAddr = key.DestAddr
   182  	key.DestAddr = addr
   183  	valMap, err := m.Lookup(&key)
   184  	if err == nil {
   185  		val := *(*NatEntry4)(unsafe.Pointer(valMap.GetValuePtr()))
   186  		rkey := key
   187  		rkey.SourceAddr = key.DestAddr
   188  		rkey.SourcePort = key.DestPort
   189  		rkey.DestAddr = val.Addr
   190  		rkey.DestPort = val.Port
   191  		rkey.Flags = tuple.TUPLE_F_IN
   192  
   193  		m.Delete(&key)
   194  		m.Delete(&rkey)
   195  	}
   196  	return nil
   197  }
   198  
   199  func deleteMapping6(m *Map, ctKey *tuple.TupleKey6Global) error {
   200  	key := NatKey6{
   201  		TupleKey6Global: *ctKey,
   202  	}
   203  	// Workaround #5848.
   204  	addr := key.SourceAddr
   205  	key.SourceAddr = key.DestAddr
   206  	key.DestAddr = addr
   207  	valMap, err := m.Lookup(&key)
   208  	if err == nil {
   209  		val := *(*NatEntry6)(unsafe.Pointer(valMap.GetValuePtr()))
   210  		rkey := key
   211  		rkey.SourceAddr = key.DestAddr
   212  		rkey.SourcePort = key.DestPort
   213  		rkey.DestAddr = val.Addr
   214  		rkey.DestPort = val.Port
   215  		rkey.Flags = tuple.TUPLE_F_IN
   216  
   217  		m.Delete(&key)
   218  		m.Delete(&rkey)
   219  	}
   220  	return nil
   221  }
   222  
   223  // DeleteMapping removes a NAT mapping from the global NAT table.
   224  func (m *Map) DeleteMapping(key tuple.TupleKey) error {
   225  	if key.GetFlags()&tuple.TUPLE_F_IN != 0 {
   226  		return nil
   227  	}
   228  	if m.v4 {
   229  		return deleteMapping4(m, key.(*tuple.TupleKey4Global))
   230  	}
   231  	return deleteMapping6(m, key.(*tuple.TupleKey6Global))
   232  }
   233  
   234  // GlobalMaps returns all global NAT maps.
   235  func GlobalMaps(ipv4, ipv6 bool) (ipv4Map, ipv6Map *Map) {
   236  	entries := option.Config.NATMapEntriesGlobal
   237  	if entries == 0 {
   238  		entries = option.LimitTableMax
   239  	}
   240  	if ipv4 {
   241  		ipv4Map = NewMap(MapNameSnat4Global, true, entries)
   242  	}
   243  	if ipv6 {
   244  		ipv6Map = NewMap(MapNameSnat6Global, false, entries)
   245  	}
   246  	return
   247  }