github.com/cilium/cilium@v1.16.2/pkg/hubble/metrics/dns/handler.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Hubble 3 4 package dns 5 6 import ( 7 "context" 8 "fmt" 9 "strings" 10 11 "github.com/prometheus/client_golang/prometheus" 12 13 flowpb "github.com/cilium/cilium/api/v1/flow" 14 "github.com/cilium/cilium/pkg/hubble/metrics/api" 15 ) 16 17 type dnsHandler struct { 18 includeQuery bool 19 ignoreAAAA bool 20 21 context *api.ContextOptions 22 23 queries *prometheus.CounterVec 24 responses *prometheus.CounterVec 25 responseTypes *prometheus.CounterVec 26 } 27 28 func (d *dnsHandler) Init(registry *prometheus.Registry, options api.Options) error { 29 c, err := api.ParseContextOptions(options) 30 if err != nil { 31 return err 32 } 33 d.context = c 34 35 for key := range options { 36 switch strings.ToLower(key) { 37 case "query": 38 d.includeQuery = true 39 case "ignoreaaaa": 40 d.ignoreAAAA = true 41 } 42 } 43 44 contextLabels := d.context.GetLabelNames() 45 commonLabels := append(contextLabels, "rcode", "qtypes") 46 queryAndResponseLabels := append(commonLabels, "ips_returned") 47 responseTypeLabels := append(contextLabels, "type", "qtypes") 48 49 if d.includeQuery { 50 queryAndResponseLabels = append(queryAndResponseLabels, "query") 51 responseTypeLabels = append(responseTypeLabels, "query") 52 } 53 54 d.queries = prometheus.NewCounterVec(prometheus.CounterOpts{ 55 Namespace: api.DefaultPrometheusNamespace, 56 Name: "dns_queries_total", 57 Help: "Number of DNS queries observed", 58 }, queryAndResponseLabels) 59 registry.MustRegister(d.queries) 60 61 d.responses = prometheus.NewCounterVec(prometheus.CounterOpts{ 62 Namespace: api.DefaultPrometheusNamespace, 63 Name: "dns_responses_total", 64 Help: "Number of DNS queries observed", 65 }, queryAndResponseLabels) 66 registry.MustRegister(d.responses) 67 68 d.responseTypes = prometheus.NewCounterVec(prometheus.CounterOpts{ 69 Namespace: api.DefaultPrometheusNamespace, 70 Name: "dns_response_types_total", 71 Help: "Number of DNS queries observed", 72 }, responseTypeLabels) 73 registry.MustRegister(d.responseTypes) 74 75 return nil 76 } 77 78 func (d *dnsHandler) Status() string { 79 var status []string 80 if d.includeQuery { 81 status = append(status, "query") 82 } 83 if d.ignoreAAAA { 84 status = append(status, "ignoreAAAA") 85 } 86 87 return strings.Join(append(status, d.context.Status()), ",") 88 } 89 90 func (d *dnsHandler) Context() *api.ContextOptions { 91 return d.context 92 } 93 94 func (d *dnsHandler) ListMetricVec() []*prometheus.MetricVec { 95 return []*prometheus.MetricVec{d.queries.MetricVec, d.responses.MetricVec, d.responseTypes.MetricVec} 96 } 97 98 func (d *dnsHandler) ProcessFlow(ctx context.Context, flow *flowpb.Flow) error { 99 if flow.GetL7() == nil { 100 return nil 101 } 102 103 dns := flow.GetL7().GetDns() 104 if dns == nil { 105 return nil 106 } 107 108 if d.ignoreAAAA && len(dns.Qtypes) == 1 && dns.Qtypes[0] == "AAAA" { 109 return nil 110 } 111 112 contextLabels, err := d.context.GetLabelValues(flow) 113 if err != nil { 114 return err 115 } 116 117 rcode := "" 118 qtypes := strings.Join(dns.Qtypes, ",") 119 ipsReturned := fmt.Sprintf("%d", len(dns.Ips)) 120 121 switch { 122 case flow.GetVerdict() == flowpb.Verdict_DROPPED: 123 rcode = "Policy denied" 124 labels := append(contextLabels, rcode, qtypes, ipsReturned) 125 if d.includeQuery { 126 labels = append(labels, dns.Query) 127 } 128 d.queries.WithLabelValues(labels...).Inc() 129 case !flow.GetIsReply().GetValue(): // dns request 130 labels := append(contextLabels, rcode, qtypes, ipsReturned) 131 if d.includeQuery { 132 labels = append(labels, dns.Query) 133 } 134 d.queries.WithLabelValues(labels...).Inc() 135 case flow.GetIsReply().GetValue(): // dns response 136 rcode = rcodeNames[dns.Rcode] 137 labels := append(contextLabels, rcode, qtypes, ipsReturned) 138 if d.includeQuery { 139 labels = append(labels, dns.Query) 140 } 141 d.responses.WithLabelValues(labels...).Inc() 142 143 for _, responseType := range dns.Rrtypes { 144 newLabels := append(contextLabels, responseType, qtypes) 145 if d.includeQuery { 146 newLabels = append(newLabels, dns.Query) 147 } 148 d.responseTypes.WithLabelValues(newLabels...).Inc() 149 } 150 } 151 152 return nil 153 }