github.com/fafucoder/cilium@v1.6.11/pkg/signal/signal.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 signal
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/binary"
    20  	"fmt"
    21  	"runtime"
    22  	"sync"
    23  
    24  	"github.com/cilium/cilium/pkg/bpf"
    25  	"github.com/cilium/cilium/pkg/byteorder"
    26  	"github.com/cilium/cilium/pkg/logging"
    27  	"github.com/cilium/cilium/pkg/logging/logfields"
    28  	"github.com/cilium/cilium/pkg/metrics"
    29  )
    30  
    31  const (
    32  	// SignalMapName is the BPF map name
    33  	SignalMapName = "cilium_signals"
    34  )
    35  
    36  const (
    37  	// SignalNatFillUp denotes potential congestion on the NAT table
    38  	SignalNatFillUp = iota
    39  	SignalTypeMax
    40  )
    41  
    42  const (
    43  	// SignalNatV4 denotes NAT IPv4 table
    44  	SignalNatV4 = iota
    45  	// SignalNatV6 denotes NAT IPv6 table
    46  	SignalNatV6
    47  	SignalNatMax
    48  )
    49  
    50  // SignalData holds actual data the BPF program sent along with
    51  // the signal. Can be extended upon need for new signals.
    52  type SignalData uint32
    53  
    54  // SignalMsg is the message we receive from BPF datapath
    55  type SignalMsg struct {
    56  	Which uint32
    57  	Data  SignalData
    58  }
    59  
    60  var (
    61  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "signal")
    62  
    63  	config = bpf.PerfEventConfig{
    64  		MapName:      SignalMapName,
    65  		Type:         bpf.PERF_TYPE_SOFTWARE,
    66  		Config:       bpf.PERF_COUNT_SW_BPF_OUTPUT,
    67  		SampleType:   bpf.PERF_SAMPLE_RAW,
    68  		NumPages:     1,
    69  		WakeupEvents: 1,
    70  	}
    71  
    72  	channels [SignalTypeMax]chan<- SignalData
    73  )
    74  
    75  var (
    76  	signalName = [SignalTypeMax]string{
    77  		SignalNatFillUp: "nat_fill_up",
    78  	}
    79  
    80  	signalNatProto = [SignalNatMax]string{
    81  		SignalNatV4: "ipv4",
    82  		SignalNatV6: "ipv6",
    83  	}
    84  )
    85  
    86  func signalCollectMetrics(sig *SignalMsg, signalStatus string) {
    87  	signalType := ""
    88  	signalData := ""
    89  	if sig != nil {
    90  		signalType = signalName[sig.Which]
    91  		if sig.Which == SignalNatFillUp {
    92  			signalData = signalNatProto[sig.Data]
    93  		}
    94  	}
    95  	metrics.SignalsHandled.WithLabelValues(signalType, signalData, signalStatus).Inc()
    96  }
    97  
    98  func signalReceive(msg *bpf.PerfEventSample, cpu int) {
    99  	sig := SignalMsg{}
   100  	if err := binary.Read(bytes.NewReader(msg.DataDirect()), byteorder.Native, &sig); err != nil {
   101  		log.WithError(err).Warningf("Cannot parse signal from BPF datapath")
   102  		return
   103  	}
   104  	if channels[sig.Which] != nil {
   105  		channels[sig.Which] <- sig.Data
   106  		signalCollectMetrics(&sig, "received")
   107  	}
   108  }
   109  
   110  func signalLost(lost *bpf.PerfEventLost, cpu int) {
   111  	// Not much we can do here, with the given set of signals it is non-fatal,
   112  	// so we keep ignoring lost events right now.
   113  	signalCollectMetrics(nil, "lost")
   114  }
   115  
   116  func signalError(err *bpf.PerfEvent) {
   117  	signalCollectMetrics(nil, "error")
   118  
   119  	log.Errorf("BUG: Timeout while reading signal perf ring buffer: %s", err.Debug())
   120  }
   121  
   122  // MuteChannel tells to not send any new events to a particular channel
   123  // for a given signal.
   124  func MuteChannel(signal int) error {
   125  	if signal != SignalNatFillUp {
   126  		return fmt.Errorf("Signal number not supported: %d", signal)
   127  	}
   128  	// Right now we only support 1 type of signal, we may extend this in
   129  	// future. If all signals are muted, then we can simply turn off perf
   130  	// RB notifications from kernel side, which is much more efficient as
   131  	// no new message is pushed into the RB.
   132  	if events != nil {
   133  		events.Mute()
   134  	}
   135  	return nil
   136  }
   137  
   138  // UnmuteChannel tells to allow sending new events to a particular channel
   139  // for a given signal.
   140  func UnmuteChannel(signal int) error {
   141  	if signal != SignalNatFillUp {
   142  		return fmt.Errorf("Signal number not supported: %d", signal)
   143  	}
   144  	// See comment in MuteChannel().
   145  	if events != nil {
   146  		events.Unmute()
   147  	}
   148  	return nil
   149  }
   150  
   151  // RegisterChannel registers a go channel for a given signal.
   152  func RegisterChannel(signal int, ch chan<- SignalData) error {
   153  	if signal >= SignalTypeMax {
   154  		return fmt.Errorf("Signal number not supported: %d", signal)
   155  	}
   156  	if channels[signal] != nil {
   157  		return fmt.Errorf("Channel for signal number already registered: %d", signal)
   158  	}
   159  
   160  	channels[signal] = ch
   161  	return nil
   162  }
   163  
   164  var once sync.Once
   165  var events *bpf.PerCpuEvents
   166  
   167  // SetupSignalListener bootstraps signal listener infrastructure.
   168  func SetupSignalListener() {
   169  	once.Do(func() {
   170  		var err error
   171  		config.NumCpus = runtime.NumCPU()
   172  		events, err = bpf.NewPerCpuEvents(&config)
   173  		if err != nil {
   174  			log.WithError(err).Warningf("Cannot open %s map! Ignoring signals!",
   175  				SignalMapName)
   176  			return
   177  		}
   178  
   179  		go func() {
   180  			log.Info("Datapath signal listener running")
   181  			for {
   182  				todo, err := events.Poll(-1)
   183  				if err != nil {
   184  					log.WithError(err).Warningf("%s poll error!",
   185  						SignalMapName)
   186  					continue
   187  				}
   188  				if todo > 0 {
   189  					events.ReadAll(signalReceive, signalLost, signalError)
   190  				}
   191  			}
   192  		}()
   193  	})
   194  }