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 }