xref: /aosp_15_r20/external/wmediumd/wmediumd/config.c (revision 621120a22a0cd8ba80b131fe8bcb37c86ff453e3)
1 /*
2  *	wmediumd, wireless medium simulator for mac80211_hwsim kernel module
3  *	Copyright (c) 2011 cozybit Inc.
4  *	Copyright (C) 2020 Intel Corporation
5  *
6  *	Author: Javier Lopez    <[email protected]>
7  *		Javier Cardona  <[email protected]>
8  *
9  *	This program is free software; you can redistribute it and/or
10  *	modify it under the terms of the GNU General Public License
11  *	as published by the Free Software Foundation; either version 2
12  *	of the License, or (at your option) any later version.
13  *
14  *	This program is distributed in the hope that it will be useful,
15  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *	GNU General Public License for more details.
18  *
19  *	You should have received a copy of the GNU General Public License
20  *	along with this program; if not, write to the Free Software
21  *	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  *	02110-1301, USA.
23  */
24 
25 #include <sys/timerfd.h>
26 #include <libconfig.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <math.h>
31 
32 #include "wmediumd.h"
33 #include "config.h"
34 
35 #ifndef MIN
36 #define MIN(a, b) ((a) < (b) ? (a) : (b))
37 #endif
38 
39 #ifndef MAX
40 #define MAX(a, b) ((a) > (b) ? (a) : (b))
41 #endif
42 
string_to_mac_address(const char * str,u8 * addr)43 static void string_to_mac_address(const char *str, u8 *addr)
44 {
45 	int a[ETH_ALEN];
46 
47 	sscanf(str, "%x:%x:%x:%x:%x:%x",
48 	       &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]);
49 
50 	addr[0] = (u8) a[0];
51 	addr[1] = (u8) a[1];
52 	addr[2] = (u8) a[2];
53 	addr[3] = (u8) a[3];
54 	addr[4] = (u8) a[4];
55 	addr[5] = (u8) a[5];
56 }
57 
get_link_snr_default(struct wmediumd * ctx,struct station * sender,struct station * receiver)58 static int get_link_snr_default(struct wmediumd *ctx, struct station *sender,
59 				 struct station *receiver)
60 {
61 	return SNR_DEFAULT;
62 }
63 
get_link_snr_from_snr_matrix(struct wmediumd * ctx,struct station * sender,struct station * receiver)64 static int get_link_snr_from_snr_matrix(struct wmediumd *ctx,
65 					struct station *sender,
66 					struct station *receiver)
67 {
68 	return ctx->snr_matrix[sender->index * ctx->num_stas + receiver->index];
69 }
70 
_get_error_prob_from_snr(struct wmediumd * ctx,double snr,unsigned int rate_idx,u32 freq,int frame_len,struct station * src,struct station * dst)71 static double _get_error_prob_from_snr(struct wmediumd *ctx, double snr,
72 				       unsigned int rate_idx, u32 freq,
73 				       int frame_len,
74 				       struct station *src, struct station *dst)
75 {
76 	return get_error_prob_from_snr(snr, rate_idx, freq, frame_len);
77 }
78 
get_error_prob_from_matrix(struct wmediumd * ctx,double snr,unsigned int rate_idx,u32 freq,int frame_len,struct station * src,struct station * dst)79 static double get_error_prob_from_matrix(struct wmediumd *ctx, double snr,
80 					 unsigned int rate_idx, u32 freq,
81 					 int frame_len, struct station *src,
82 					 struct station *dst)
83 {
84 	if (dst == NULL) // dst is multicast. returned value will not be used.
85 		return 0.0;
86 
87 	return ctx->error_prob_matrix[ctx->num_stas * src->index + dst->index];
88 }
89 
use_fixed_random_value(struct wmediumd * ctx)90 int use_fixed_random_value(struct wmediumd *ctx)
91 {
92 	return ctx->error_prob_matrix != NULL;
93 }
94 
95 #define FREQ_1CH (2.412e9)		// [Hz]
96 #define SPEED_LIGHT (2.99792458e8)	// [meter/sec]
97 /*
98  * Calculate path loss based on a free-space path loss
99  *
100  * This function returns path loss [dBm].
101  */
calc_path_loss_free_space(void * model_param,struct station * dst,struct station * src)102 static int calc_path_loss_free_space(void *model_param,
103 			  struct station *dst, struct station *src)
104 {
105 	double PL, d;
106 
107 	d = sqrt((src->x - dst->x) * (src->x - dst->x) +
108 		 (src->y - dst->y) * (src->y - dst->y));
109 
110 	/*
111 	 * Calculate PL0 with Free-space path loss in decibels
112 	 *
113 	 * 20 * log10 * (4 * M_PI * d * f / c)
114 	 *   d: distance [meter]
115 	 *   f: frequency [Hz]
116 	 *   c: speed of light in a vacuum [meter/second]
117 	 *
118 	 * https://en.wikipedia.org/wiki/Free-space_path_loss
119 	 */
120 	PL = 20.0 * log10(4.0 * M_PI * d * FREQ_1CH / SPEED_LIGHT);
121 	return MAX(PL, 0);
122 }
123 /*
124  * Calculate path loss based on a log distance model
125  *
126  * This function returns path loss [dBm].
127  */
calc_path_loss_log_distance(void * model_param,struct station * dst,struct station * src)128 static int calc_path_loss_log_distance(void *model_param,
129 			  struct station *dst, struct station *src)
130 {
131 	struct log_distance_model_param *param;
132 	double PL, PL0, d;
133 
134 	param = model_param;
135 
136 	d = sqrt((src->x - dst->x) * (src->x - dst->x) +
137 		 (src->y - dst->y) * (src->y - dst->y));
138 
139 	/*
140 	 * Calculate PL0 with Free-space path loss in decibels
141 	 *
142 	 * 20 * log10 * (4 * M_PI * d * f / c)
143 	 *   d: distance [meter]
144 	 *   f: frequency [Hz]
145 	 *   c: speed of light in a vacuum [meter/second]
146 	 *
147 	 * https://en.wikipedia.org/wiki/Free-space_path_loss
148 	 */
149 	PL0 = 20.0 * log10(4.0 * M_PI * 1.0 * FREQ_1CH / SPEED_LIGHT);
150 
151 	/*
152 	 * Calculate signal strength with Log-distance path loss model
153 	 * https://en.wikipedia.org/wiki/Log-distance_path_loss_model
154 	 */
155 	PL = PL0 + 10.0 * param->path_loss_exponent * log10(d) + param->Xg;
156 
157 	return MAX(PL, 0);
158 }
159 /*
160  * Calculate path loss based on a itu model
161  *
162  * This function returns path loss [dBm].
163  */
calc_path_loss_itu(void * model_param,struct station * dst,struct station * src)164 static int calc_path_loss_itu(void *model_param,
165 			  struct station *dst, struct station *src)
166 {
167 	struct itu_model_param *param;
168 	double PL, d;
169 	int N=28;
170 
171 	param = model_param;
172 
173 	d = sqrt((src->x - dst->x) * (src->x - dst->x) +
174 		 (src->y - dst->y) * (src->y - dst->y));
175 
176 	if (d>16)
177 		N=38;
178 	/*
179 	 * Calculate signal strength with ITU path loss model
180 	 * Power Loss Coefficient Based on the Paper
181          * Site-Specific Validation of ITU Indoor Path Loss Model at 2.4 GHz
182          * from Theofilos Chrysikos, Giannis Georgopoulos and Stavros Kotsopoulos
183 	 * LF: floor penetration loss factor
184 	 * nFLOORS: number of floors
185 	 */
186 	PL = 20.0 * log10(FREQ_1CH) + N * log10(d) + param->LF * param->nFLOORS - 28;
187 	return MAX(PL, 0);
188 }
189 
recalc_path_loss(struct wmediumd * ctx)190 static void recalc_path_loss(struct wmediumd *ctx)
191 {
192 	int start, end, tx_power, path_loss, signal;
193 	for (start = 0; start < ctx->num_stas; start++) {
194 		for (end = 0; end < ctx->num_stas; end++) {
195 			if (start == end) {
196 				continue;
197 			}
198 
199 			tx_power = ctx->sta_array[start]->tx_power;
200 			path_loss = ctx->calc_path_loss(ctx->path_loss_param, ctx->sta_array[end], ctx->sta_array[start]);
201 			// Test breakage exists in WifiStatsTests when signal is not negative value.
202 			signal = MIN(tx_power - path_loss, -1);
203 			ctx->snr_matrix[ctx->num_stas * start + end] = signal - NOISE_LEVEL;
204 		}
205 	}
206 }
207 
move_stations_to_direction(struct usfstl_job * job)208 static void move_stations_to_direction(struct usfstl_job *job)
209 {
210 	struct wmediumd *ctx = job->data;
211 	struct station *station;
212 
213 	list_for_each_entry(station, &ctx->stations, list) {
214 		station->x += station->dir_x;
215 		station->y += station->dir_y;
216 	}
217 	recalc_path_loss(ctx);
218 
219 	job->start += MOVE_INTERVAL * 1000000;
220 	usfstl_sched_add_job(&scheduler, job);
221 }
222 
parse_path_loss(struct wmediumd * ctx,config_t * cf)223 static int parse_path_loss(struct wmediumd *ctx, config_t *cf)
224 {
225 	struct station *station;
226 	const config_setting_t *positions, *position;
227 	const config_setting_t *directions, *direction;
228 	const config_setting_t *tx_powers, *model;
229 	const char *path_loss_model_name;
230 
231 	positions = config_lookup(cf, "model.positions");
232 	if (!positions) {
233 		w_flogf(ctx, LOG_ERR, stderr,
234 			"No positions found in model\n");
235 		return -EINVAL;
236 	}
237 	if (config_setting_length(positions) != ctx->num_stas) {
238 		w_flogf(ctx, LOG_ERR, stderr,
239 			"Specify %d positions\n", ctx->num_stas);
240 		return -EINVAL;
241 	}
242 
243 	directions = config_lookup(cf, "model.directions");
244 	if (directions) {
245 		if (config_setting_length(directions) != ctx->num_stas) {
246 			w_flogf(ctx, LOG_ERR, stderr,
247 				"Specify %d directions\n", ctx->num_stas);
248 			return -EINVAL;
249 		}
250 		ctx->move_job.start = MOVE_INTERVAL * 1000000;
251 		ctx->move_job.name = "move";
252 		ctx->move_job.data = ctx;
253 		ctx->move_job.callback = move_stations_to_direction;
254 		usfstl_sched_add_job(&scheduler, &ctx->move_job);
255 	}
256 
257 	tx_powers = config_lookup(cf, "model.tx_powers");
258 	if (!tx_powers) {
259 		w_flogf(ctx, LOG_ERR, stderr,
260 			"No tx_powers found in model\n");
261 		return -EINVAL;
262 	}
263 	if (config_setting_length(tx_powers) != ctx->num_stas) {
264 		w_flogf(ctx, LOG_ERR, stderr,
265 			"Specify %d tx_powers\n", ctx->num_stas);
266 		return -EINVAL;
267 	}
268 
269 	model = config_lookup(cf, "model");
270 	if (config_setting_lookup_string(model, "model_name",
271 		&path_loss_model_name) != CONFIG_TRUE) {
272 		w_flogf(ctx, LOG_ERR, stderr, "Specify model_name\n");
273 		return -EINVAL;
274 	}
275 	if (strncmp(path_loss_model_name, "log_distance",
276 		    sizeof("log_distance")) == 0) {
277 		struct log_distance_model_param *param;
278 
279 		ctx->calc_path_loss = calc_path_loss_log_distance;
280 
281 		param = malloc(sizeof(*param));
282 		if (!param) {
283 			w_flogf(ctx, LOG_ERR, stderr,
284 				"Out of memory(path_loss_param)\n");
285 			return -EINVAL;
286 		}
287 
288 		if (config_setting_lookup_float(model, "path_loss_exp",
289 			&param->path_loss_exponent) != CONFIG_TRUE) {
290 			w_flogf(ctx, LOG_ERR, stderr,
291 				"path_loss_exponent not found\n");
292 			return -EINVAL;
293 		}
294 
295 		if (config_setting_lookup_float(model, "xg",
296 			&param->Xg) != CONFIG_TRUE) {
297 			w_flogf(ctx, LOG_ERR, stderr, "xg not found\n");
298 			return -EINVAL;
299 		}
300 		ctx->path_loss_param = param;
301 	}
302 	else if (strncmp(path_loss_model_name, "free_space",
303 			sizeof("free_space")) == 0) {
304 		struct log_distance_model_param *param;
305 		ctx->calc_path_loss = calc_path_loss_free_space;
306 		param = malloc(sizeof(*param));
307 		if (!param) {
308 			w_flogf(ctx, LOG_ERR, stderr,
309 				"Out of memory(path_loss_param)\n");
310 			return -EINVAL;
311 		}
312 	}
313 	else if (strncmp(path_loss_model_name, "itu",
314 			sizeof("itu")) == 0) {
315 		struct itu_model_param *param;
316 		ctx->calc_path_loss = calc_path_loss_itu;
317 		param = malloc(sizeof(*param));
318 		if (!param) {
319 			w_flogf(ctx, LOG_ERR, stderr,
320 				"Out of memory(path_loss_param)\n");
321 			return -EINVAL;
322 		}
323 
324 		if (config_setting_lookup_int(model, "nFLOORS",
325 			&param->nFLOORS) != CONFIG_TRUE) {
326 			w_flogf(ctx, LOG_ERR, stderr,
327 				"nFLOORS not found\n");
328 			return -EINVAL;
329 		}
330 
331 		if (config_setting_lookup_int(model, "LF",
332 			&param->LF) != CONFIG_TRUE) {
333 			w_flogf(ctx, LOG_ERR, stderr, "LF not found\n");
334 			return -EINVAL;
335 		}
336 		ctx->path_loss_param = param;
337 	}
338 	else {
339 		w_flogf(ctx, LOG_ERR, stderr, "No path loss model found\n");
340 		return -EINVAL;
341 	}
342 
343 	list_for_each_entry(station, &ctx->stations, list) {
344 		position = config_setting_get_elem(positions, station->index);
345 		if (config_setting_length(position) != 2) {
346 			w_flogf(ctx, LOG_ERR, stderr,
347 				"Invalid position: expected (double,double)\n");
348 			return -EINVAL;
349 		}
350 		station->x = config_setting_get_float_elem(position, 0);
351 		station->y = config_setting_get_float_elem(position, 1);
352 
353 		if (directions) {
354 			direction = config_setting_get_elem(directions,
355 				station->index);
356 			if (config_setting_length(direction) != 2) {
357 				w_flogf(ctx, LOG_ERR, stderr,
358 					"Invalid direction: expected (double,double)\n");
359 				return -EINVAL;
360 			}
361 			station->dir_x = config_setting_get_float_elem(
362 				direction, 0);
363 			station->dir_y = config_setting_get_float_elem(
364 				direction, 1);
365 		}
366 
367 		station->tx_power = config_setting_get_float_elem(
368 			tx_powers, station->index);
369 	}
370 
371 	recalc_path_loss(ctx);
372 
373 	return 0;
374 }
375 
pseudo_normal_distribution(void)376 static double pseudo_normal_distribution(void)
377 {
378 	int i;
379 	double normal = -6.0;
380 
381 	for (i = 0; i < 12; i++)
382 		normal += drand48();
383 
384 	return normal;
385 }
386 
_get_fading_signal(struct wmediumd * ctx)387 static int _get_fading_signal(struct wmediumd *ctx)
388 {
389 	return ctx->fading_coefficient * pseudo_normal_distribution();
390 }
391 
get_no_fading_signal(struct wmediumd * ctx)392 static int get_no_fading_signal(struct wmediumd *ctx)
393 {
394 	return 0;
395 }
396 
397 /* Existing link is from from -> to; copy to other dir */
mirror_link(struct wmediumd * ctx,int from,int to)398 static void mirror_link(struct wmediumd *ctx, int from, int to)
399 {
400 	ctx->snr_matrix[ctx->num_stas * to + from] =
401 		ctx->snr_matrix[ctx->num_stas * from + to];
402 
403 	if (ctx->error_prob_matrix) {
404 		ctx->error_prob_matrix[ctx->num_stas * to + from] =
405 			ctx->error_prob_matrix[ctx->num_stas * from + to];
406 	}
407 }
408 
validate_config(const char * file)409 int validate_config(const char* file) {
410 	struct wmediumd ctx = {};
411 
412 	INIT_LIST_HEAD(&ctx.stations);
413 	INIT_LIST_HEAD(&ctx.clients);
414 	INIT_LIST_HEAD(&ctx.clients_to_free);
415 
416 	int load_result = load_config(&ctx, file, NULL);
417 
418 	clear_config(&ctx);
419 
420 	if (load_result < 0) return 0;
421 
422 	return 1;
423 }
424 
425 /*
426  *	Loads a config file into memory
427  */
load_config(struct wmediumd * ctx,const char * file,const char * per_file)428 int load_config(struct wmediumd *ctx, const char *file, const char *per_file)
429 {
430 	config_t cfg, *cf;
431 	const config_setting_t *ids, *links, *model_type;
432 	const config_setting_t *error_probs = NULL, *error_prob;
433 	const config_setting_t *enable_interference;
434 	const config_setting_t *fading_coefficient, *default_prob;
435 	int count_ids, i, j;
436 	int start, end, snr;
437 	struct station *station;
438 	const char *model_type_str;
439 	float default_prob_value = 0.0;
440 	bool *link_map = NULL;
441 
442 	ctx->config_path = strdup(file);
443 
444 	/*initialize the config file*/
445 	cf = &cfg;
446 	config_init(cf);
447 
448 	/*read the file*/
449 	if (!config_read_file(cf, file)) {
450 		w_logf(ctx, LOG_ERR, "Error loading file %s at line:%d, reason: %s\n",
451 				file,
452 				config_error_line(cf),
453 				config_error_text(cf));
454 		config_destroy(cf);
455 		return -EIO;
456 	}
457 
458 	ids = config_lookup(cf, "ifaces.ids");
459 	if (!ids) {
460 		w_logf(ctx, LOG_ERR, "ids not found in config file\n");
461 		return -EIO;
462 	}
463 	count_ids = config_setting_length(ids);
464 
465 	w_logf(ctx, LOG_NOTICE, "#_if = %d\n", count_ids);
466 
467 	/* Fill the mac_addr */
468 	ctx->sta_array = calloc(count_ids, sizeof(struct station *));
469 	if (!ctx->sta_array) {
470 		w_flogf(ctx, LOG_ERR, stderr, "Out of memory(sta_array)!\n");
471 		return -ENOMEM;
472 	}
473 	for (i = 0; i < count_ids; i++) {
474 		u8 addr[ETH_ALEN];
475 		const char *str =  config_setting_get_string_elem(ids, i);
476 		string_to_mac_address(str, addr);
477 
478 		station = calloc(1, sizeof(*station));
479 		if (!station) {
480 			w_flogf(ctx, LOG_ERR, stderr, "Out of memory!\n");
481 			return -ENOMEM;
482 		}
483 		station->index = i;
484 		memcpy(station->addr, addr, ETH_ALEN);
485 		memcpy(station->hwaddr, addr, ETH_ALEN);
486 		station->tx_power = SNR_DEFAULT;
487 		station_init_queues(station);
488 		list_add_tail(&station->list, &ctx->stations);
489 		ctx->sta_array[i] = station;
490 
491 		w_logf(ctx, LOG_NOTICE, "Added station %d: " MAC_FMT "\n", i, MAC_ARGS(addr));
492 	}
493 	ctx->num_stas = count_ids;
494 
495 	enable_interference = config_lookup(cf, "ifaces.enable_interference");
496 	if (enable_interference &&
497 	    config_setting_get_bool(enable_interference)) {
498 		ctx->intf = calloc(ctx->num_stas * ctx->num_stas,
499 				   sizeof(struct intf_info));
500 		if (!ctx->intf) {
501 			w_flogf(ctx, LOG_ERR, stderr, "Out of memory(intf)\n");
502 			return -ENOMEM;
503 		}
504 		for (i = 0; i < ctx->num_stas; i++)
505 			for (j = 0; j < ctx->num_stas; j++)
506 				ctx->intf[i * ctx->num_stas + j].signal = -200;
507 	} else {
508 		ctx->intf = NULL;
509 	}
510 
511 	fading_coefficient =
512 		config_lookup(cf, "model.fading_coefficient");
513 	if (fading_coefficient &&
514 	    config_setting_get_int(fading_coefficient) > 0) {
515 		ctx->get_fading_signal = _get_fading_signal;
516 		ctx->fading_coefficient =
517 			config_setting_get_int(fading_coefficient);
518 	} else {
519 		ctx->get_fading_signal = get_no_fading_signal;
520 		ctx->fading_coefficient = 0;
521 	}
522 
523 	/* create link quality matrix */
524 	ctx->snr_matrix = calloc(sizeof(int), count_ids * count_ids);
525 	if (!ctx->snr_matrix) {
526 		w_flogf(ctx, LOG_ERR, stderr, "Out of memory!\n");
527 		return -ENOMEM;
528 	}
529 	/* set default snrs */
530 	for (i = 0; i < count_ids * count_ids; i++)
531 		ctx->snr_matrix[i] = SNR_DEFAULT;
532 
533 	links = config_lookup(cf, "ifaces.links");
534 	if (!links) {
535 		model_type = config_lookup(cf, "model.type");
536 		if (model_type) {
537 			model_type_str = config_setting_get_string(model_type);
538 			if (memcmp("snr", model_type_str, strlen("snr")) == 0) {
539 				links = config_lookup(cf, "model.links");
540 			} else if (memcmp("prob", model_type_str,
541 				strlen("prob")) == 0) {
542 				error_probs = config_lookup(cf, "model.links");
543 			} else if (memcmp("path_loss", model_type_str,
544 				strlen("path_loss")) == 0) {
545 				/* calculate signal from positions */
546 				if (parse_path_loss(ctx, cf))
547 					goto fail;
548 			}
549 		}
550 	}
551 
552 	if (per_file && error_probs) {
553 		w_flogf(ctx, LOG_ERR, stderr,
554 			"per_file and error_probs could not be used at the same time\n");
555 		goto fail;
556 	}
557 
558 	ctx->get_link_snr = get_link_snr_from_snr_matrix;
559 	ctx->get_error_prob = _get_error_prob_from_snr;
560 
561 	ctx->per_matrix = NULL;
562 	ctx->per_matrix_row_num = 0;
563 	if (per_file && read_per_file(ctx, per_file))
564 		goto fail;
565 
566 	ctx->error_prob_matrix = NULL;
567 	if (error_probs) {
568 		ctx->error_prob_matrix = calloc(sizeof(double),
569 						count_ids * count_ids);
570 		if (!ctx->error_prob_matrix) {
571 			w_flogf(ctx, LOG_ERR, stderr,
572 				"Out of memory(error_prob_matrix)\n");
573 			goto fail;
574 		}
575 
576 		ctx->get_link_snr = get_link_snr_default;
577 		ctx->get_error_prob = get_error_prob_from_matrix;
578 
579 		default_prob = config_lookup(cf, "model.default_prob");
580 		if (default_prob) {
581 			default_prob_value = config_setting_get_float(
582 				default_prob);
583 			if (default_prob_value < 0.0 ||
584 			    default_prob_value > 1.0) {
585 				w_flogf(ctx, LOG_ERR, stderr,
586 					"model.default_prob should be in [0.0, 1.0]\n");
587 				goto fail;
588 			}
589 		}
590 	}
591 
592 	link_map = calloc(sizeof(bool), count_ids * count_ids);
593 	if (!link_map) {
594 		w_flogf(ctx, LOG_ERR, stderr, "Out of memory\n");
595 		goto fail;
596 	}
597 
598 	/* read snr values */
599 	for (i = 0; links && i < config_setting_length(links); i++) {
600 		config_setting_t *link;
601 
602 		link = config_setting_get_elem(links, i);
603 		if (config_setting_length(link) != 3) {
604 			w_flogf(ctx, LOG_ERR, stderr, "Invalid link: expected (int,int,int)\n");
605 			goto fail;
606 		}
607 		start = config_setting_get_int_elem(link, 0);
608 		end = config_setting_get_int_elem(link, 1);
609 		snr = config_setting_get_int_elem(link, 2);
610 
611 		if (start < 0 || start >= ctx->num_stas ||
612 		    end < 0 || end >= ctx->num_stas) {
613 			w_flogf(ctx, LOG_ERR, stderr, "Invalid link [%d,%d,%d]: index out of range\n",
614 					start, end, snr);
615 			goto fail;
616 		}
617 		ctx->snr_matrix[ctx->num_stas * start + end] = snr;
618 		link_map[ctx->num_stas * start + end] = true;
619 	}
620 
621 	/* initialize with default_prob */
622 	for (start = 0; error_probs && start < ctx->num_stas; start++)
623 		for (end = start + 1; end < ctx->num_stas; end++) {
624 			ctx->error_prob_matrix[ctx->num_stas *
625 				start + end] =
626 			ctx->error_prob_matrix[ctx->num_stas *
627 				end + start] = default_prob_value;
628 		}
629 
630 	/* read error probabilities */
631 	for (i = 0; error_probs &&
632 	     i < config_setting_length(error_probs); i++) {
633 		float error_prob_value;
634 
635 		error_prob = config_setting_get_elem(error_probs, i);
636 		if (config_setting_length(error_prob) != 3) {
637 			w_flogf(ctx, LOG_ERR, stderr, "Invalid error probability: expected (int,int,float)\n");
638 			goto fail;
639 		}
640 
641 		start = config_setting_get_int_elem(error_prob, 0);
642 		end = config_setting_get_int_elem(error_prob, 1);
643 		error_prob_value = config_setting_get_float_elem(error_prob, 2);
644 
645 		if (start < 0 || start >= ctx->num_stas ||
646 		    end < 0 || end >= ctx->num_stas ||
647 		    error_prob_value < 0.0 || error_prob_value > 1.0) {
648 			w_flogf(ctx, LOG_ERR, stderr, "Invalid error probability [%d,%d,%f]\n",
649 				start, end, error_prob_value);
650 			goto fail;
651 		}
652 
653 		ctx->error_prob_matrix[ctx->num_stas * start + end] =
654 			error_prob_value;
655 		link_map[ctx->num_stas * start + end] = true;
656 	}
657 
658 	/*
659 	 * If any links are specified in only one direction, mirror them,
660 	 * making them symmetric.  If specified in both directions they
661 	 * can be asymmetric.
662 	 */
663 	for (i = 0; i < ctx->num_stas; i++) {
664 		for (j = 0; j < ctx->num_stas; j++) {
665 			if (i == j)
666 				continue;
667 
668 			if (link_map[ctx->num_stas * i + j] &&
669 			    !link_map[ctx->num_stas * j + i]) {
670 				mirror_link(ctx, i, j);
671 			}
672 		}
673 	}
674 
675 	free(link_map);
676 	config_destroy(cf);
677 	return 0;
678 
679 fail:
680 	free(ctx->snr_matrix);
681 	free(ctx->error_prob_matrix);
682 	config_destroy(cf);
683 	return -EINVAL;
684 }
685 
clear_config(struct wmediumd * ctx)686 int clear_config(struct wmediumd *ctx) {
687 	free(ctx->sta_array);
688 	free(ctx->intf);
689 	free(ctx->snr_matrix);
690 	free(ctx->error_prob_matrix);
691 	free(ctx->config_path);
692 
693 	ctx->sta_array = NULL;
694 	ctx->intf = NULL;
695 	ctx->snr_matrix = NULL;
696 	ctx->error_prob_matrix = NULL;
697 	ctx->config_path = NULL;
698 
699 	while (!list_empty(&ctx->stations)) {
700 		struct station *station;
701 
702 		station = list_first_entry(&ctx->stations,
703 					    struct station, list);
704 
705 		list_del(&station->list);
706 
707 		free(station->lci);
708 		free(station->civicloc);
709 		free(station);
710 	}
711 
712 	return 0;
713 }
714 
calc_path_loss(struct wmediumd * ctx)715 void calc_path_loss(struct wmediumd *ctx) {
716 	recalc_path_loss(ctx);
717 }