github.com/fafucoder/cilium@v1.6.11/cilium/cmd/bpf_ipcache_get.go (about)

     1  // Copyright 2018 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 cmd
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"os"
    21  	"strings"
    22  
    23  	"github.com/cilium/cilium/common"
    24  	"github.com/cilium/cilium/pkg/maps/ipcache"
    25  
    26  	"github.com/hashicorp/go-immutable-radix"
    27  	"github.com/spf13/cobra"
    28  )
    29  
    30  const usage = "IP address must be in dotted decimal (192.168.1.1) or IPv6 (feab::f02b) form"
    31  
    32  var bpfIPCacheGetCmd = &cobra.Command{
    33  	Use:   "get",
    34  	Short: "Retrieve identity for an ip",
    35  	Run: func(cmd *cobra.Command, args []string) {
    36  		common.RequireRootPrivilege("cilium bpf ipcache get")
    37  
    38  		if len(args) < 1 || args[0] == "" {
    39  			Usagef(cmd, "No ip provided. "+usage)
    40  		}
    41  
    42  		arg := args[0]
    43  
    44  		ip := net.ParseIP(arg)
    45  		if ip == nil {
    46  			Usagef(cmd, "Invalid ip address. "+usage)
    47  		}
    48  
    49  		bpfIPCache := dumpIPCache()
    50  
    51  		if len(bpfIPCache) == 0 {
    52  			fmt.Fprintf(os.Stderr, "No entries found.\n")
    53  			os.Exit(1)
    54  		}
    55  
    56  		value, exists := getLPMValue(ip, bpfIPCache)
    57  
    58  		if !exists {
    59  			fmt.Printf("%s does not map to any identity\n", arg)
    60  			os.Exit(1)
    61  		}
    62  
    63  		v := value.([]string)
    64  		if len(v) == 0 {
    65  			fmt.Printf("Unable to retrieve identity for LPM entry %s\n", arg)
    66  			os.Exit(1)
    67  		}
    68  
    69  		ids := strings.Join(v, ",")
    70  		fmt.Printf("%s maps to identity %s\n", arg, ids)
    71  	},
    72  }
    73  
    74  func init() {
    75  	bpfIPCacheCmd.AddCommand(bpfIPCacheGetCmd)
    76  }
    77  
    78  func dumpIPCache() map[string][]string {
    79  	bpfIPCache := make(map[string][]string)
    80  
    81  	if err := ipcache.IPCache.Dump(bpfIPCache); err != nil {
    82  		Fatalf("unable to dump IPCache: %s\n", err)
    83  	}
    84  
    85  	return bpfIPCache
    86  }
    87  
    88  // getLPMValue calculates the longest prefix matching ip amongst the
    89  // keys in entries. The keys in entries must be specified in CIDR notation.
    90  // If LPM is found, the value associated with that entry is returned
    91  // along with boolean true. Otherwise, false is returned.
    92  func getLPMValue(ip net.IP, entries map[string][]string) (interface{}, bool) {
    93  	type lpmEntry struct {
    94  		prefix   []byte
    95  		identity []string
    96  	}
    97  
    98  	isV4 := isIPV4(ip)
    99  
   100  	// Convert ip to 4-byte representation if IPv4.
   101  	if isV4 {
   102  		ip = ip.To4()
   103  	}
   104  
   105  	var lpmEntries []lpmEntry
   106  	for cidr, identity := range entries {
   107  		currIP, subnet, err := net.ParseCIDR(cidr)
   108  
   109  		if err != nil {
   110  			log.Warnf("unable to parse ipcache entry '%s' as a CIDR: %s", cidr, err)
   111  			continue
   112  		}
   113  
   114  		// No need to include IPv6 addresses if the argument is
   115  		// IPv4 and vice versa.
   116  		if isIPV4(currIP) != isV4 {
   117  			continue
   118  		}
   119  
   120  		// Convert ip to 4-byte representation if IPv4.
   121  		if isV4 {
   122  			currIP = currIP.To4()
   123  		}
   124  
   125  		ones, _ := subnet.Mask.Size()
   126  		prefix := getPrefix(currIP, ones)
   127  
   128  		lpmEntries = append(lpmEntries, lpmEntry{prefix, identity})
   129  	}
   130  
   131  	r := iradix.New()
   132  	for _, e := range lpmEntries {
   133  		r, _, _ = r.Insert(e.prefix, e.identity)
   134  	}
   135  
   136  	// Look-up using all bits in the argument ip
   137  	var mask int
   138  	if isV4 {
   139  		mask = 8 * net.IPv4len
   140  	} else {
   141  		mask = 8 * net.IPv6len
   142  	}
   143  
   144  	_, v, exists := r.Root().LongestPrefix(getPrefix(ip, mask))
   145  	return v, exists
   146  }
   147  
   148  // getPrefix converts the most significant maskSize bits in ip
   149  // into a byte slice - each bit is represented using one byte.
   150  func getPrefix(ip net.IP, maskSize int) []byte {
   151  	bytes := make([]byte, maskSize)
   152  	var i, j uint8
   153  	var n int
   154  
   155  	for n < maskSize {
   156  		for j = 0; j < 8 && n < maskSize; j++ {
   157  			mask := uint8(128) >> uint8(j)
   158  
   159  			if mask&ip[i] == 0 {
   160  				bytes[i*8+j] = 0x0
   161  			} else {
   162  				bytes[i*8+j] = 0x1
   163  			}
   164  			n++
   165  		}
   166  		i++
   167  	}
   168  
   169  	return bytes
   170  }
   171  
   172  func isIPV4(ip net.IP) bool {
   173  	return ip.To4() != nil
   174  }