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  }