1 /*
2  * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos
3  * Copyright (C) 2015, 2016 Red Hat, Inc.
4  *
5  * This file is part of ocserv.
6  *
7  * ocserv is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * ocserv is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 
23 #include <gnutls/gnutls.h>
24 #include <gnutls/dtls.h>
25 #include <math.h>
26 #include <linux/net_tstamp.h>
27 #include <linux/errqueue.h>
28 #include <worker.h>
29 #include <worker-latency.h>
30 
31 
dtls_pull_latency(gnutls_transport_ptr_t ptr,void * data,size_t size)32 ssize_t dtls_pull_latency(gnutls_transport_ptr_t ptr, void *data, size_t size)
33 {
34 	int err;
35 	dtls_transport_ptr *p = ptr;
36 	p->rx_time.tv_sec = 0;
37 	p->rx_time.tv_nsec = 0;
38 
39 	if (p->msg) {
40 		ssize_t need = p->msg->data.len;
41 		if (need > size) {
42 			need = size;
43 		}
44 		memcpy(data, p->msg->data.data, need);
45 
46 		udp_fd_msg__free_unpacked(p->msg, NULL);
47 		p->msg = NULL;
48 		return need;
49 	}
50 
51 	char controlbuf[1024];
52 	struct cmsghdr * cmsg;
53 
54 	struct iovec io = {
55 		.iov_base = data,
56 		.iov_len = size,
57 	};
58 	struct msghdr hdr = {
59 		.msg_iov = &io,
60 		.msg_iovlen = 1,
61 		.msg_control = controlbuf,
62 		.msg_controllen = sizeof(controlbuf)
63 	};
64 	err = recvmsg(p->fd, &hdr, 0);
65 	if (err >= 0) {
66 		for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL; cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
67 			struct scm_timestamping *tss = NULL;
68 			if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_TIMESTAMPING) {
69 				continue;
70 			}
71 			tss = (struct scm_timestamping *) CMSG_DATA(cmsg);
72 			p->rx_time = tss->ts[0];
73 		}
74 	}
75 	return err;
76 }
77 
78 
send_latency_stats_delta_to_main(worker_st * ws,time_t now)79 void send_latency_stats_delta_to_main(worker_st * ws, time_t now)
80 {
81 	LatencyStatsDelta msg = LATENCY_STATS_DELTA__INIT;
82 
83 	if (ws->latency.sample_set_count == 0) {
84 		return;
85 	}
86 
87 	msg.median_delta = ws->latency.median_total;
88 	msg.rms_delta = ws->latency.rms_total;
89 	msg.sample_count_delta = ws->latency.sample_set_count;
90 
91 	ws->latency.median_total = 0;
92 	ws->latency.rms_total = 0;
93 	ws->latency.sample_set_count = 0;
94 
95 	send_msg_to_main(ws, CMD_LATENCY_STATS_DELTA, &msg,
96 			 (pack_size_func) latency_stats_delta__get_packed_size,
97 			 (pack_func) latency_stats_delta__pack);
98 
99 	ws->latency.last_stats_msg = now;
100 }
101 
greater_than(const void * a,const void * b)102 static int greater_than(const void * a, const void * b)
103 {
104     const unsigned long lhs = *(const unsigned long*)a;
105     const unsigned long rhs = *(const unsigned long*)b;
106     return rhs - lhs;
107 }
108 
capture_latency_sample(struct worker_st * ws,struct timespec * processing_start_time)109 void capture_latency_sample(struct worker_st* ws, struct timespec *processing_start_time)
110 {
111 	struct timespec now;
112 	gettime_realtime(&now);
113 	unsigned long sample = (unsigned long)timespec_sub_us(&now, processing_start_time);
114 	if (ws->latency.next_sample == LATENCY_SAMPLE_SIZE) {
115 		unsigned long median;
116 		uint64_t total = 0;
117 		long double sum_of_squares = 0;
118 		uint64_t mean = 0;
119 		uint64_t rms = 0;
120 		int i;
121 
122 		ws->latency.next_sample = 0;
123 		qsort(ws->latency.samples, LATENCY_SAMPLE_SIZE, sizeof(ws->latency.samples[0]), greater_than);
124 		median = ws->latency.samples[LATENCY_SAMPLE_SIZE - 1];
125 
126 		for (i = 0; i < LATENCY_SAMPLE_SIZE; i ++) {
127 			total += ws->latency.samples[i];
128 		}
129 
130 		mean = total / LATENCY_SAMPLE_SIZE;
131 		for (i = 0; i < LATENCY_SAMPLE_SIZE; i ++) {
132 			long double delta = (long double)ws->latency.samples[i];
133 			delta -= mean;
134 			sum_of_squares += delta * delta;
135 		}
136 
137 		rms = (uint64_t)sqrt(sum_of_squares / LATENCY_SAMPLE_SIZE);
138 
139 		(ws->latency.median_total) += median;
140 		(ws->latency.rms_total) += rms;
141 		(ws->latency.sample_set_count) ++;
142     }
143     ws->latency.samples[(ws->latency.next_sample)++] = sample;
144 
145 }
146