xref: /dragonfly/sbin/ifconfig/regdomain.c (revision d4e390fc)
1 /*-
2  * Copyright (c) 2008 Sam Leffler, Errno Consulting
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * $FreeBSD: head/sbin/ifconfig/regdomain.c 200587 2009-12-15 20:44:12Z gavin $
26  */
27 
28 #include <sys/types.h>
29 #include <sys/errno.h>
30 #include <sys/mman.h>
31 #include <sys/sbuf.h>
32 #include <sys/stat.h>
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <fcntl.h>
38 #include <err.h>
39 #include <unistd.h>
40 
41 #include <bsdxml.h>
42 
43 #include "regdomain.h"
44 
45 #include <netproto/802_11/_ieee80211.h>
46 
47 #define	MAXLEVEL	20
48 
49 struct mystate {
50 	XML_Parser		parser;
51 	struct regdata		*rdp;
52 	struct regdomain	*rd;		/* current domain */
53 	struct netband		*netband;	/* current netband */
54 	struct freqband		*freqband;	/* current freqband */
55 	struct country		*country;	/* current country */
56 	netband_head		*curband;	/* current netband list */
57 	int			level;
58 	struct sbuf		*sbuf[MAXLEVEL];
59 	int			nident;
60 };
61 
62 struct ident {
63 	const void *id;
64 	void *p;
65 	enum { DOMAIN, COUNTRY, FREQBAND } type;
66 };
67 
68 static void
69 start_element(void *data, const char *name, const char **attr)
70 {
71 #define	iseq(a,b)	(strcasecmp(a,b) == 0)
72 	struct mystate *mt;
73 	const void *id, *ref, *mode;
74 	int i;
75 
76 	mt = data;
77 	if (++mt->level == MAXLEVEL) {
78 		/* XXX force parser to abort */
79 		return;
80 	}
81 	mt->sbuf[mt->level] = sbuf_new_auto();
82 	id = ref = mode = NULL;
83 	for (i = 0; attr[i] != NULL; i += 2) {
84 		if (iseq(attr[i], "id")) {
85 			id = attr[i+1];
86 		} else if (iseq(attr[i], "ref")) {
87 			ref = attr[i+1];
88 		} else if (iseq(attr[i], "mode")) {
89 			mode = attr[i+1];
90 		} else
91 			printf("%*.*s[%s = %s]\n", mt->level + 1,
92 			    mt->level + 1, "", attr[i], attr[i+1]);
93 	}
94 	if (iseq(name, "rd") && mt->rd == NULL) {
95 		if (mt->country == NULL) {
96 			mt->rd = calloc(1, sizeof(struct regdomain));
97 			mt->rd->name = strdup(id);
98 			mt->nident++;
99 			LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
100 		} else
101 			mt->country->rd = (void *)strdup(ref);
102 		return;
103 	}
104 	if (iseq(name, "defcc") && mt->rd != NULL) {
105 		mt->rd->cc = (void *)strdup(ref);
106 		return;
107 	}
108 	if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
109 		if (mode == NULL) {
110 			warnx("no mode for netband at line %ld",
111 			    XML_GetCurrentLineNumber(mt->parser));
112 			return;
113 		}
114 		if (iseq(mode, "11b"))
115 			mt->curband = &mt->rd->bands_11b;
116 		else if (iseq(mode, "11g"))
117 			mt->curband = &mt->rd->bands_11g;
118 		else if (iseq(mode, "11a"))
119 			mt->curband = &mt->rd->bands_11a;
120 		else if (iseq(mode, "11ng"))
121 			mt->curband = &mt->rd->bands_11ng;
122 		else if (iseq(mode, "11na"))
123 			mt->curband = &mt->rd->bands_11na;
124 		else
125 			warnx("unknown mode \"%s\" at line %ld",
126 			    __DECONST(char *, mode),
127 			    XML_GetCurrentLineNumber(mt->parser));
128 		return;
129 	}
130 	if (iseq(name, "band") && mt->netband == NULL) {
131 		if (mt->curband == NULL) {
132 			warnx("band without enclosing netband at line %ld",
133 			    XML_GetCurrentLineNumber(mt->parser));
134 			return;
135 		}
136 		mt->netband = calloc(1, sizeof(struct netband));
137 		LIST_INSERT_HEAD(mt->curband, mt->netband, next);
138 		return;
139 	}
140 	if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) {
141 		/* XXX handle inlines and merge into table? */
142 		if (mt->netband->band != NULL) {
143 			warnx("duplicate freqband at line %ld ignored",
144 			    XML_GetCurrentLineNumber(mt->parser));
145 			/* XXX complain */
146 		} else
147 			mt->netband->band = (void *)strdup(ref);
148 		return;
149 	}
150 
151 	if (iseq(name, "country") && mt->country == NULL) {
152 		mt->country = calloc(1, sizeof(struct country));
153 		mt->country->isoname = strdup(id);
154 		mt->country->code = NO_COUNTRY;
155 		mt->nident++;
156 		LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
157 		return;
158 	}
159 
160 	if (iseq(name, "freqband") && mt->freqband == NULL) {
161 		mt->freqband = calloc(1, sizeof(struct freqband));
162 		mt->freqband->id = strdup(id);
163 		mt->nident++;
164 		LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
165 		return;
166 	}
167 #undef iseq
168 }
169 
170 static int
171 decode_flag(struct mystate *mt, const char *p, int len)
172 {
173 #define	iseq(a,b)	(strcasecmp(a,b) == 0)
174 	static const struct {
175 		const char *name;
176 		int len;
177 		uint32_t value;
178 	} flags[] = {
179 #define	FLAG(x)	{ #x, sizeof(#x)-1, x }
180 		FLAG(IEEE80211_CHAN_A),
181 		FLAG(IEEE80211_CHAN_B),
182 		FLAG(IEEE80211_CHAN_G),
183 		FLAG(IEEE80211_CHAN_HT20),
184 		FLAG(IEEE80211_CHAN_HT40),
185 		FLAG(IEEE80211_CHAN_ST),
186 		FLAG(IEEE80211_CHAN_TURBO),
187 		FLAG(IEEE80211_CHAN_PASSIVE),
188 		FLAG(IEEE80211_CHAN_DFS),
189 		FLAG(IEEE80211_CHAN_CCK),
190 		FLAG(IEEE80211_CHAN_OFDM),
191 		FLAG(IEEE80211_CHAN_2GHZ),
192 		FLAG(IEEE80211_CHAN_5GHZ),
193 		FLAG(IEEE80211_CHAN_DYN),
194 		FLAG(IEEE80211_CHAN_GFSK),
195 		FLAG(IEEE80211_CHAN_GSM),
196 		FLAG(IEEE80211_CHAN_STURBO),
197 		FLAG(IEEE80211_CHAN_HALF),
198 		FLAG(IEEE80211_CHAN_QUARTER),
199 		FLAG(IEEE80211_CHAN_HT40U),
200 		FLAG(IEEE80211_CHAN_HT40D),
201 		FLAG(IEEE80211_CHAN_4MSXMIT),
202 		FLAG(IEEE80211_CHAN_NOADHOC),
203 		FLAG(IEEE80211_CHAN_NOHOSTAP),
204 		FLAG(IEEE80211_CHAN_11D),
205 		FLAG(IEEE80211_CHAN_FHSS),
206 		FLAG(IEEE80211_CHAN_PUREG),
207 		FLAG(IEEE80211_CHAN_108A),
208 		FLAG(IEEE80211_CHAN_108G),
209 #undef FLAG
210 		{ "ECM",	3,	REQ_ECM },
211 		{ "INDOOR",	6,	REQ_INDOOR },
212 		{ "OUTDOOR",	7,	REQ_OUTDOOR },
213 	};
214 	int i;
215 
216 	for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++)
217 		if (len == flags[i].len && iseq(p, flags[i].name))
218 			return flags[i].value;
219 	warnx("unknown flag \"%.*s\" at line %ld ignored",
220 	    len, p, XML_GetCurrentLineNumber(mt->parser));
221 	return 0;
222 #undef iseq
223 }
224 
225 static void
226 end_element(void *data, const char *name)
227 {
228 #define	iseq(a,b)	(strcasecmp(a,b) == 0)
229 	struct mystate *mt;
230 	int len;
231 	char *p;
232 
233 	mt = data;
234 	sbuf_finish(mt->sbuf[mt->level]);
235 	p = sbuf_data(mt->sbuf[mt->level]);
236 	len = sbuf_len(mt->sbuf[mt->level]);
237 
238 	/* <freqband>...</freqband> */
239 	if (iseq(name, "freqstart") && mt->freqband != NULL) {
240 		mt->freqband->freqStart = strtoul(p, NULL, 0);
241 		goto done;
242 	}
243 	if (iseq(name, "freqend") && mt->freqband != NULL) {
244 		mt->freqband->freqEnd = strtoul(p, NULL, 0);
245 		goto done;
246 	}
247 	if (iseq(name, "chanwidth") && mt->freqband != NULL) {
248 		mt->freqband->chanWidth = strtoul(p, NULL, 0);
249 		goto done;
250 	}
251 	if (iseq(name, "chansep") && mt->freqband != NULL) {
252 		mt->freqband->chanSep = strtoul(p, NULL, 0);
253 		goto done;
254 	}
255 	if (iseq(name, "flags")) {
256 		if (mt->freqband != NULL)
257 			mt->freqband->flags |= decode_flag(mt, p, len);
258 		else if (mt->netband != NULL)
259 			mt->netband->flags |= decode_flag(mt, p, len);
260 		else {
261 			warnx("flags without freqband or netband at line %ld ignored",
262 			    XML_GetCurrentLineNumber(mt->parser));
263 		}
264 		goto done;
265 	}
266 
267 	/* <rd> ... </rd> */
268 	if (iseq(name, "name") && mt->rd != NULL) {
269 		mt->rd->name = strdup(p);
270 		goto done;
271 	}
272 	if (iseq(name, "sku") && mt->rd != NULL) {
273 		mt->rd->sku = strtoul(p, NULL, 0);
274 		goto done;
275 	}
276 	if (iseq(name, "netband") && mt->rd != NULL) {
277 		mt->curband = NULL;
278 		goto done;
279 	}
280 
281 	/* <band> ... </band> */
282 	if (iseq(name, "freqband") && mt->netband != NULL) {
283 		/* XXX handle inline freqbands */
284 		goto done;
285 	}
286 	if (iseq(name, "maxpower") && mt->netband != NULL) {
287 		mt->netband->maxPower = strtoul(p, NULL, 0);
288 		goto done;
289 	}
290 	if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
291 		mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
292 		goto done;
293 	}
294 	if (iseq(name, "maxantgain") && mt->netband != NULL) {
295 		mt->netband->maxAntGain = strtoul(p, NULL, 0);
296 		goto done;
297 	}
298 
299 	/* <country>...</country> */
300 	if (iseq(name, "isocc") && mt->country != NULL) {
301 		mt->country->code = strtoul(p, NULL, 0);
302 		goto done;
303 	}
304 	if (iseq(name, "name") && mt->country != NULL) {
305 		mt->country->name = strdup(p);
306 		goto done;
307 	}
308 
309 	if (len != 0) {
310 		warnx("unexpected XML token \"%s\" data \"%s\" at line %ld",
311 		    name, p, XML_GetCurrentLineNumber(mt->parser));
312 		/* XXX goto done? */
313 	}
314 	/* </freqband> */
315 	if (iseq(name, "freqband") && mt->freqband != NULL) {
316 		/* XXX must have start/end frequencies */
317 		/* XXX must have channel width/sep */
318 		mt->freqband = NULL;
319 		goto done;
320 	}
321 	/* </rd> */
322 	if (iseq(name, "rd") && mt->rd != NULL) {
323 		mt->rd = NULL;
324 		goto done;
325 	}
326 	/* </band> */
327 	if (iseq(name, "band") && mt->netband != NULL) {
328 		if (mt->netband->band == NULL) {
329 			warnx("no freqbands for band at line %ld",
330 			   XML_GetCurrentLineNumber(mt->parser));
331 		}
332 		if (mt->netband->maxPower == 0) {
333 			warnx("no maxpower for band at line %ld",
334 			   XML_GetCurrentLineNumber(mt->parser));
335 		}
336 		/* default max power w/ DFS to max power */
337 		if (mt->netband->maxPowerDFS == 0)
338 			mt->netband->maxPowerDFS = mt->netband->maxPower;
339 		mt->netband = NULL;
340 		goto done;
341 	}
342 	/* </netband> */
343 	if (iseq(name, "netband") && mt->netband != NULL) {
344 		mt->curband = NULL;
345 		goto done;
346 	}
347 	/* </country> */
348 	if (iseq(name, "country") && mt->country != NULL) {
349 		if (mt->country->code == NO_COUNTRY) {
350 			warnx("no ISO cc for country at line %ld",
351 			   XML_GetCurrentLineNumber(mt->parser));
352 		}
353 		if (mt->country->name == NULL) {
354 			warnx("no name for country at line %ld",
355 			   XML_GetCurrentLineNumber(mt->parser));
356 		}
357 		if (mt->country->rd == NULL) {
358 			warnx("no regdomain reference for country at line %ld",
359 			   XML_GetCurrentLineNumber(mt->parser));
360 		}
361 		mt->country = NULL;
362 		goto done;
363 	}
364 done:
365 	sbuf_delete(mt->sbuf[mt->level]);
366 	mt->sbuf[mt->level--] = NULL;
367 #undef iseq
368 }
369 
370 static void
371 char_data(void *data, const XML_Char *s, int len)
372 {
373 	struct mystate *mt;
374 	const char *b, *e;
375 
376 	mt = data;
377 
378 	b = s;
379 	e = s + len-1;
380 	for (; isspace(*b) && b < e; b++)
381 		;
382 	for (; isspace(*e) && e > b; e++)
383 		;
384 	if (e != b || (*b != '\0' && !isspace(*b)))
385 		sbuf_bcat(mt->sbuf[mt->level], b, e-b+1);
386 }
387 
388 static void *
389 findid(struct regdata *rdp, const void *id, int type)
390 {
391 	struct ident *ip;
392 
393 	for (ip = rdp->ident; ip->id != NULL; ip++)
394 		if (ip->type == type && strcasecmp(ip->id, id) == 0)
395 			return ip->p;
396 	return NULL;
397 }
398 
399 /*
400  * Parse an regdomain XML configuration and build the internal representation.
401  */
402 int
403 lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len)
404 {
405 	struct mystate *mt;
406 	struct regdomain *dp;
407 	struct country *cp;
408 	struct freqband *fp;
409 	struct netband *nb;
410 	const void *id;
411 	int i, errors;
412 
413 	memset(rdp, 0, sizeof(struct regdata));
414 	mt = calloc(1, sizeof(struct mystate));
415 	if (mt == NULL)
416 		return ENOMEM;
417 	/* parse the XML input */
418 	mt->rdp = rdp;
419 	mt->parser = XML_ParserCreate(NULL);
420 	XML_SetUserData(mt->parser, mt);
421 	XML_SetElementHandler(mt->parser, start_element, end_element);
422 	XML_SetCharacterDataHandler(mt->parser, char_data);
423 	if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) {
424 		warnx("%s: %s at line %ld", __func__,
425 		   XML_ErrorString(XML_GetErrorCode(mt->parser)),
426 		   XML_GetCurrentLineNumber(mt->parser));
427 		return -1;
428 	}
429 	XML_ParserFree(mt->parser);
430 
431 	/* setup the identifer table */
432 	rdp->ident = calloc(sizeof(struct ident), mt->nident + 1);
433 	if (rdp->ident == NULL)
434 		return ENOMEM;
435 	free(mt);
436 
437 	errors = 0;
438 	i = 0;
439 	LIST_FOREACH(dp, &rdp->domains, next) {
440 		rdp->ident[i].id = dp->name;
441 		rdp->ident[i].p = dp;
442 		rdp->ident[i].type = DOMAIN;
443 		i++;
444 	}
445 	LIST_FOREACH(fp, &rdp->freqbands, next) {
446 		rdp->ident[i].id = fp->id;
447 		rdp->ident[i].p = fp;
448 		rdp->ident[i].type = FREQBAND;
449 		i++;
450 	}
451 	LIST_FOREACH(cp, &rdp->countries, next) {
452 		rdp->ident[i].id = cp->isoname;
453 		rdp->ident[i].p = cp;
454 		rdp->ident[i].type = COUNTRY;
455 		i++;
456 	}
457 
458 	/* patch references */
459 	LIST_FOREACH(dp, &rdp->domains, next) {
460 		if (dp->cc != NULL) {
461 			id = dp->cc;
462 			dp->cc = findid(rdp, id, COUNTRY);
463 			if (dp->cc == NULL) {
464 				warnx("undefined country \"%s\"",
465 				    __DECONST(char *, id));
466 				errors++;
467 			}
468 			free(__DECONST(char *, id));
469 		}
470 		LIST_FOREACH(nb, &dp->bands_11b, next) {
471 			id = findid(rdp, nb->band, FREQBAND);
472 			if (id == NULL) {
473 				warnx("undefined 11b band \"%s\"",
474 				    __DECONST(char *, nb->band));
475 				errors++;
476 			}
477 			nb->band = id;
478 		}
479 		LIST_FOREACH(nb, &dp->bands_11g, next) {
480 			id = findid(rdp, nb->band, FREQBAND);
481 			if (id == NULL) {
482 				warnx("undefined 11g band \"%s\"",
483 				    __DECONST(char *, nb->band));
484 				errors++;
485 			}
486 			nb->band = id;
487 		}
488 		LIST_FOREACH(nb, &dp->bands_11a, next) {
489 			id = findid(rdp, nb->band, FREQBAND);
490 			if (id == NULL) {
491 				warnx("undefined 11a band \"%s\"",
492 				    __DECONST(char *, nb->band));
493 				errors++;
494 			}
495 			nb->band = id;
496 		}
497 		LIST_FOREACH(nb, &dp->bands_11ng, next) {
498 			id = findid(rdp, nb->band, FREQBAND);
499 			if (id == NULL) {
500 				warnx("undefined 11ng band \"%s\"",
501 				    __DECONST(char *, nb->band));
502 				errors++;
503 			}
504 			nb->band = id;
505 		}
506 		LIST_FOREACH(nb, &dp->bands_11na, next) {
507 			id = findid(rdp, nb->band, FREQBAND);
508 			if (id == NULL) {
509 				warnx("undefined 11na band \"%s\"",
510 				    __DECONST(char *, nb->band));
511 				errors++;
512 			}
513 			nb->band = id;
514 		}
515 	}
516 	LIST_FOREACH(cp, &rdp->countries, next) {
517 		id = cp->rd;
518 		cp->rd = findid(rdp, id, DOMAIN);
519 		if (cp->rd == NULL) {
520 			warnx("undefined country \"%s\"",
521 			    __DECONST(char *, id));
522 			errors++;
523 		}
524 		free(__DECONST(char *, id));
525 	}
526 
527 	return errors ? EINVAL : 0;
528 }
529 
530 static void
531 cleanup_bands(netband_head *head)
532 {
533 	struct netband *nb;
534 
535 	for (;;) {
536 		nb = LIST_FIRST(head);
537 		if (nb == NULL)
538 			break;
539 		free(nb);
540 	}
541 }
542 
543 /*
544  * Cleanup state/resources for a previously parsed regdomain database.
545  */
546 void
547 lib80211_regdomain_cleanup(struct regdata *rdp)
548 {
549 
550 	free(rdp->ident);
551 	rdp->ident = NULL;
552 	for (;;) {
553 		struct regdomain *dp = LIST_FIRST(&rdp->domains);
554 		if (dp == NULL)
555 			break;
556 		LIST_REMOVE(dp, next);
557 		cleanup_bands(&dp->bands_11b);
558 		cleanup_bands(&dp->bands_11g);
559 		cleanup_bands(&dp->bands_11a);
560 		cleanup_bands(&dp->bands_11ng);
561 		cleanup_bands(&dp->bands_11na);
562 		if (dp->name != NULL)
563 			free(__DECONST(char *, dp->name));
564 	}
565 	for (;;) {
566 		struct country *cp = LIST_FIRST(&rdp->countries);
567 		if (cp == NULL)
568 			break;
569 		LIST_REMOVE(cp, next);
570 		if (cp->name != NULL)
571 			free(__DECONST(char *, cp->name));
572 		free(cp);
573 	}
574 	for (;;) {
575 		struct freqband *fp = LIST_FIRST(&rdp->freqbands);
576 		if (fp == NULL)
577 			break;
578 		LIST_REMOVE(fp, next);
579 		free(fp);
580 	}
581 }
582 
583 struct regdata *
584 lib80211_alloc_regdata(void)
585 {
586 	struct regdata *rdp;
587 	struct stat sb;
588 	void *xml;
589 	int fd;
590 
591 	rdp = calloc(1, sizeof(struct regdata));
592 
593 	fd = open(_PATH_REGDOMAIN, O_RDONLY);
594 	if (fd < 0) {
595 #ifdef DEBUG
596 		warn("%s: open(%s)", __func__, _PATH_REGDOMAIN);
597 #endif
598 		free(rdp);
599 		return NULL;
600 	}
601 	if (fstat(fd, &sb) < 0) {
602 #ifdef DEBUG
603 		warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN);
604 #endif
605 		close(fd);
606 		free(rdp);
607 		return NULL;
608 	}
609 	xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
610 	if (xml == MAP_FAILED) {
611 #ifdef DEBUG
612 		warn("%s: mmap", __func__);
613 #endif
614 		close(fd);
615 		free(rdp);
616 		return NULL;
617 	}
618 	if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) {
619 #ifdef DEBUG
620 		warn("%s: error reading regulatory database", __func__);
621 #endif
622 		munmap(xml, sb.st_size);
623 		close(fd);
624 		free(rdp);
625 		return NULL;
626 	}
627 	munmap(xml, sb.st_size);
628 	close(fd);
629 
630 	return rdp;
631 }
632 
633 void
634 lib80211_free_regdata(struct regdata *rdp)
635 {
636 	lib80211_regdomain_cleanup(rdp);
637 	free(rdp);
638 }
639 
640 /*
641  * Lookup a regdomain by SKU.
642  */
643 const struct regdomain *
644 lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku)
645 {
646 	const struct regdomain *dp;
647 
648 	LIST_FOREACH(dp, &rdp->domains, next) {
649 		if (dp->sku == sku)
650 			return dp;
651 	}
652 	return NULL;
653 }
654 
655 /*
656  * Lookup a regdomain by name.
657  */
658 const struct regdomain *
659 lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name)
660 {
661 	const struct regdomain *dp;
662 
663 	LIST_FOREACH(dp, &rdp->domains, next) {
664 		if (strcasecmp(dp->name, name) == 0)
665 			return dp;
666 	}
667 	return NULL;
668 }
669 
670 /*
671  * Lookup a country by ISO country code.
672  */
673 const struct country *
674 lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc)
675 {
676 	const struct country *cp;
677 
678 	LIST_FOREACH(cp, &rdp->countries, next) {
679 		if (cp->code == cc)
680 			return cp;
681 	}
682 	return NULL;
683 }
684 
685 /*
686  * Lookup a country by ISO/long name.
687  */
688 const struct country *
689 lib80211_country_findbyname(const struct regdata *rdp, const char *name)
690 {
691 	const struct country *cp;
692 	int len;
693 
694 	len = strlen(name);
695 	LIST_FOREACH(cp, &rdp->countries, next) {
696 		if (strcasecmp(cp->isoname, name) == 0)
697 			return cp;
698 	}
699 	LIST_FOREACH(cp, &rdp->countries, next) {
700 		if (strncasecmp(cp->name, name, len) == 0)
701 			return cp;
702 	}
703 	return NULL;
704 }
705