1178354Ssam/*-
2178354Ssam * Copyright (c) 2008 Sam Leffler, Errno Consulting
3178354Ssam * All rights reserved.
4178354Ssam *
5178354Ssam * Redistribution and use in source and binary forms, with or without
6178354Ssam * modification, are permitted provided that the following conditions
7178354Ssam * are met:
8178354Ssam * 1. Redistributions of source code must retain the above copyright
9178354Ssam *    notice, this list of conditions and the following disclaimer.
10178354Ssam * 2. Redistributions in binary form must reproduce the above copyright
11178354Ssam *    notice, this list of conditions and the following disclaimer in the
12178354Ssam *    documentation and/or other materials provided with the distribution.
13178354Ssam *
14178354Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15178354Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16178354Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17178354Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18178354Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19178354Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20178354Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21178354Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22178354Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23178354Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24178354Ssam */
25178354Ssam#ifndef lint
26178354Ssamstatic const char rcsid[] = "$FreeBSD$";
27178354Ssam#endif /* not lint */
28178354Ssam
29178354Ssam#include <sys/types.h>
30178354Ssam#include <sys/errno.h>
31178354Ssam#include <sys/mman.h>
32178354Ssam#include <sys/sbuf.h>
33178354Ssam#include <sys/stat.h>
34178354Ssam
35178354Ssam#include <stdio.h>
36178354Ssam#include <string.h>
37178354Ssam#include <ctype.h>
38178354Ssam#include <fcntl.h>
39178354Ssam#include <err.h>
40178354Ssam#include <unistd.h>
41178354Ssam
42178354Ssam#include <bsdxml.h>
43178354Ssam
44178354Ssam#include "regdomain.h"
45178354Ssam
46178354Ssam#include <net80211/_ieee80211.h>
47178354Ssam
48178354Ssam#define	MAXLEVEL	20
49178354Ssam
50178354Ssamstruct mystate {
51186100Ssam	XML_Parser		parser;
52178354Ssam	struct regdata		*rdp;
53178354Ssam	struct regdomain	*rd;		/* current domain */
54178354Ssam	struct netband		*netband;	/* current netband */
55178354Ssam	struct freqband		*freqband;	/* current freqband */
56178354Ssam	struct country		*country;	/* current country */
57178354Ssam	netband_head		*curband;	/* current netband list */
58178354Ssam	int			level;
59178354Ssam	struct sbuf		*sbuf[MAXLEVEL];
60178354Ssam	int			nident;
61178354Ssam};
62178354Ssam
63178354Ssamstruct ident {
64178354Ssam	const void *id;
65178354Ssam	void *p;
66178354Ssam	enum { DOMAIN, COUNTRY, FREQBAND } type;
67178354Ssam};
68178354Ssam
69178354Ssamstatic void
70178354Ssamstart_element(void *data, const char *name, const char **attr)
71178354Ssam{
72178354Ssam#define	iseq(a,b)	(strcasecmp(a,b) == 0)
73178354Ssam	struct mystate *mt;
74178354Ssam	const void *id, *ref, *mode;
75178354Ssam	int i;
76178354Ssam
77178354Ssam	mt = data;
78178354Ssam	if (++mt->level == MAXLEVEL) {
79178354Ssam		/* XXX force parser to abort */
80178354Ssam		return;
81178354Ssam	}
82181463Sdes	mt->sbuf[mt->level] = sbuf_new_auto();
83178354Ssam	id = ref = mode = NULL;
84178354Ssam	for (i = 0; attr[i] != NULL; i += 2) {
85178354Ssam		if (iseq(attr[i], "id")) {
86178354Ssam			id = attr[i+1];
87178354Ssam		} else if (iseq(attr[i], "ref")) {
88178354Ssam			ref = attr[i+1];
89178354Ssam		} else if (iseq(attr[i], "mode")) {
90178354Ssam			mode = attr[i+1];
91178354Ssam		} else
92178354Ssam			printf("%*.*s[%s = %s]\n", mt->level + 1,
93178354Ssam			    mt->level + 1, "", attr[i], attr[i+1]);
94178354Ssam	}
95178354Ssam	if (iseq(name, "rd") && mt->rd == NULL) {
96178354Ssam		if (mt->country == NULL) {
97178354Ssam			mt->rd = calloc(1, sizeof(struct regdomain));
98178354Ssam			mt->rd->name = strdup(id);
99178354Ssam			mt->nident++;
100178354Ssam			LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
101178354Ssam		} else
102178354Ssam			mt->country->rd = (void *)strdup(ref);
103178354Ssam		return;
104178354Ssam	}
105178354Ssam	if (iseq(name, "defcc") && mt->rd != NULL) {
106178354Ssam		mt->rd->cc = (void *)strdup(ref);
107178354Ssam		return;
108178354Ssam	}
109178354Ssam	if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
110178354Ssam		if (mode == NULL) {
111186100Ssam			warnx("no mode for netband at line %ld",
112186100Ssam			    XML_GetCurrentLineNumber(mt->parser));
113178354Ssam			return;
114178354Ssam		}
115178354Ssam		if (iseq(mode, "11b"))
116178354Ssam			mt->curband = &mt->rd->bands_11b;
117178354Ssam		else if (iseq(mode, "11g"))
118178354Ssam			mt->curband = &mt->rd->bands_11g;
119178354Ssam		else if (iseq(mode, "11a"))
120178354Ssam			mt->curband = &mt->rd->bands_11a;
121178354Ssam		else if (iseq(mode, "11ng"))
122178354Ssam			mt->curband = &mt->rd->bands_11ng;
123178354Ssam		else if (iseq(mode, "11na"))
124178354Ssam			mt->curband = &mt->rd->bands_11na;
125186100Ssam		else
126186100Ssam			warnx("unknown mode \"%s\" at line %ld",
127186100Ssam			    __DECONST(char *, mode),
128186100Ssam			    XML_GetCurrentLineNumber(mt->parser));
129178354Ssam		return;
130178354Ssam	}
131178354Ssam	if (iseq(name, "band") && mt->netband == NULL) {
132178354Ssam		if (mt->curband == NULL) {
133186100Ssam			warnx("band without enclosing netband at line %ld",
134186100Ssam			    XML_GetCurrentLineNumber(mt->parser));
135178354Ssam			return;
136178354Ssam		}
137178354Ssam		mt->netband = calloc(1, sizeof(struct netband));
138178354Ssam		LIST_INSERT_HEAD(mt->curband, mt->netband, next);
139178354Ssam		return;
140178354Ssam	}
141178354Ssam	if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) {
142178354Ssam		/* XXX handle inlines and merge into table? */
143178354Ssam		if (mt->netband->band != NULL) {
144186100Ssam			warnx("duplicate freqband at line %ld ignored",
145186100Ssam			    XML_GetCurrentLineNumber(mt->parser));
146178354Ssam			/* XXX complain */
147178354Ssam		} else
148178354Ssam			mt->netband->band = (void *)strdup(ref);
149178354Ssam		return;
150178354Ssam	}
151178354Ssam
152178354Ssam	if (iseq(name, "country") && mt->country == NULL) {
153178354Ssam		mt->country = calloc(1, sizeof(struct country));
154178354Ssam		mt->country->isoname = strdup(id);
155186100Ssam		mt->country->code = NO_COUNTRY;
156178354Ssam		mt->nident++;
157178354Ssam		LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
158178354Ssam		return;
159178354Ssam	}
160178354Ssam
161178354Ssam	if (iseq(name, "freqband") && mt->freqband == NULL) {
162178354Ssam		mt->freqband = calloc(1, sizeof(struct freqband));
163178354Ssam		mt->freqband->id = strdup(id);
164178354Ssam		mt->nident++;
165178354Ssam		LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
166178354Ssam		return;
167178354Ssam	}
168178354Ssam#undef iseq
169178354Ssam}
170178354Ssam
171186100Ssamstatic int
172186100Ssamdecode_flag(struct mystate *mt, const char *p, int len)
173178354Ssam{
174178354Ssam#define	iseq(a,b)	(strcasecmp(a,b) == 0)
175178354Ssam	static const struct {
176178354Ssam		const char *name;
177178354Ssam		int len;
178178354Ssam		uint32_t value;
179178354Ssam	} flags[] = {
180186100Ssam#define	FLAG(x)	{ #x, sizeof(#x)-1, x }
181178354Ssam		FLAG(IEEE80211_CHAN_A),
182178354Ssam		FLAG(IEEE80211_CHAN_B),
183178354Ssam		FLAG(IEEE80211_CHAN_G),
184178354Ssam		FLAG(IEEE80211_CHAN_HT20),
185178354Ssam		FLAG(IEEE80211_CHAN_HT40),
186178354Ssam		FLAG(IEEE80211_CHAN_ST),
187178354Ssam		FLAG(IEEE80211_CHAN_TURBO),
188178354Ssam		FLAG(IEEE80211_CHAN_PASSIVE),
189178354Ssam		FLAG(IEEE80211_CHAN_DFS),
190178354Ssam		FLAG(IEEE80211_CHAN_CCK),
191178354Ssam		FLAG(IEEE80211_CHAN_OFDM),
192178354Ssam		FLAG(IEEE80211_CHAN_2GHZ),
193178354Ssam		FLAG(IEEE80211_CHAN_5GHZ),
194178354Ssam		FLAG(IEEE80211_CHAN_DYN),
195178354Ssam		FLAG(IEEE80211_CHAN_GFSK),
196178354Ssam		FLAG(IEEE80211_CHAN_GSM),
197178354Ssam		FLAG(IEEE80211_CHAN_STURBO),
198178354Ssam		FLAG(IEEE80211_CHAN_HALF),
199178354Ssam		FLAG(IEEE80211_CHAN_QUARTER),
200178354Ssam		FLAG(IEEE80211_CHAN_HT40U),
201178354Ssam		FLAG(IEEE80211_CHAN_HT40D),
202178354Ssam		FLAG(IEEE80211_CHAN_4MSXMIT),
203178354Ssam		FLAG(IEEE80211_CHAN_NOADHOC),
204178354Ssam		FLAG(IEEE80211_CHAN_NOHOSTAP),
205178354Ssam		FLAG(IEEE80211_CHAN_11D),
206178354Ssam		FLAG(IEEE80211_CHAN_FHSS),
207178354Ssam		FLAG(IEEE80211_CHAN_PUREG),
208178354Ssam		FLAG(IEEE80211_CHAN_108A),
209178354Ssam		FLAG(IEEE80211_CHAN_108G),
210178354Ssam#undef FLAG
211188258Ssam		{ "ECM",	3,	REQ_ECM },
212188258Ssam		{ "INDOOR",	6,	REQ_INDOOR },
213188258Ssam		{ "OUTDOOR",	7,	REQ_OUTDOOR },
214178354Ssam	};
215178354Ssam	int i;
216178354Ssam
217178354Ssam	for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++)
218178354Ssam		if (len == flags[i].len && iseq(p, flags[i].name))
219178354Ssam			return flags[i].value;
220186100Ssam	warnx("unknown flag \"%.*s\" at line %ld ignored",
221186100Ssam	    len, p, XML_GetCurrentLineNumber(mt->parser));
222178354Ssam	return 0;
223178354Ssam#undef iseq
224178354Ssam}
225178354Ssam
226178354Ssamstatic void
227178354Ssamend_element(void *data, const char *name)
228178354Ssam{
229178354Ssam#define	iseq(a,b)	(strcasecmp(a,b) == 0)
230178354Ssam	struct mystate *mt;
231178354Ssam	int len;
232178354Ssam	char *p;
233178354Ssam
234178354Ssam	mt = data;
235178354Ssam	sbuf_finish(mt->sbuf[mt->level]);
236178354Ssam	p = sbuf_data(mt->sbuf[mt->level]);
237178354Ssam	len = sbuf_len(mt->sbuf[mt->level]);
238178354Ssam
239178354Ssam	/* <freqband>...</freqband> */
240178354Ssam	if (iseq(name, "freqstart") && mt->freqband != NULL) {
241178354Ssam		mt->freqband->freqStart = strtoul(p, NULL, 0);
242178354Ssam		goto done;
243178354Ssam	}
244178354Ssam	if (iseq(name, "freqend") && mt->freqband != NULL) {
245178354Ssam		mt->freqband->freqEnd = strtoul(p, NULL, 0);
246178354Ssam		goto done;
247178354Ssam	}
248178354Ssam	if (iseq(name, "chanwidth") && mt->freqband != NULL) {
249178354Ssam		mt->freqband->chanWidth = strtoul(p, NULL, 0);
250178354Ssam		goto done;
251178354Ssam	}
252178354Ssam	if (iseq(name, "chansep") && mt->freqband != NULL) {
253178354Ssam		mt->freqband->chanSep = strtoul(p, NULL, 0);
254178354Ssam		goto done;
255178354Ssam	}
256178354Ssam	if (iseq(name, "flags")) {
257178354Ssam		if (mt->freqband != NULL)
258186100Ssam			mt->freqband->flags |= decode_flag(mt, p, len);
259178354Ssam		else if (mt->netband != NULL)
260186100Ssam			mt->netband->flags |= decode_flag(mt, p, len);
261178354Ssam		else {
262186100Ssam			warnx("flags without freqband or netband at line %ld ignored",
263186100Ssam			    XML_GetCurrentLineNumber(mt->parser));
264178354Ssam		}
265178354Ssam		goto done;
266178354Ssam	}
267178354Ssam
268178354Ssam	/* <rd> ... </rd> */
269178354Ssam	if (iseq(name, "name") && mt->rd != NULL) {
270178354Ssam		mt->rd->name = strdup(p);
271178354Ssam		goto done;
272178354Ssam	}
273178354Ssam	if (iseq(name, "sku") && mt->rd != NULL) {
274178354Ssam		mt->rd->sku = strtoul(p, NULL, 0);
275178354Ssam		goto done;
276178354Ssam	}
277178354Ssam	if (iseq(name, "netband") && mt->rd != NULL) {
278178354Ssam		mt->curband = NULL;
279178354Ssam		goto done;
280178354Ssam	}
281178354Ssam
282178354Ssam	/* <band> ... </band> */
283178354Ssam	if (iseq(name, "freqband") && mt->netband != NULL) {
284178354Ssam		/* XXX handle inline freqbands */
285178354Ssam		goto done;
286178354Ssam	}
287178354Ssam	if (iseq(name, "maxpower") && mt->netband != NULL) {
288178354Ssam		mt->netband->maxPower = strtoul(p, NULL, 0);
289178354Ssam		goto done;
290178354Ssam	}
291178354Ssam	if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
292178354Ssam		mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
293178354Ssam		goto done;
294178354Ssam	}
295188155Ssam	if (iseq(name, "maxantgain") && mt->netband != NULL) {
296188155Ssam		mt->netband->maxAntGain = strtoul(p, NULL, 0);
297188155Ssam		goto done;
298188155Ssam	}
299178354Ssam
300178354Ssam	/* <country>...</country> */
301178354Ssam	if (iseq(name, "isocc") && mt->country != NULL) {
302178354Ssam		mt->country->code = strtoul(p, NULL, 0);
303178354Ssam		goto done;
304178354Ssam	}
305178354Ssam	if (iseq(name, "name") && mt->country != NULL) {
306178354Ssam		mt->country->name = strdup(p);
307178354Ssam		goto done;
308178354Ssam	}
309178354Ssam
310178354Ssam	if (len != 0) {
311186100Ssam		warnx("unexpected XML token \"%s\" data \"%s\" at line %ld",
312186100Ssam		    name, p, XML_GetCurrentLineNumber(mt->parser));
313178354Ssam		/* XXX goto done? */
314178354Ssam	}
315178354Ssam	/* </freqband> */
316178354Ssam	if (iseq(name, "freqband") && mt->freqband != NULL) {
317178354Ssam		/* XXX must have start/end frequencies */
318178354Ssam		/* XXX must have channel width/sep */
319178354Ssam		mt->freqband = NULL;
320178354Ssam		goto done;
321178354Ssam	}
322178354Ssam	/* </rd> */
323178354Ssam	if (iseq(name, "rd") && mt->rd != NULL) {
324178354Ssam		mt->rd = NULL;
325178354Ssam		goto done;
326178354Ssam	}
327178354Ssam	/* </band> */
328178354Ssam	if (iseq(name, "band") && mt->netband != NULL) {
329178354Ssam		if (mt->netband->band == NULL) {
330186100Ssam			warnx("no freqbands for band at line %ld",
331186100Ssam			   XML_GetCurrentLineNumber(mt->parser));
332178354Ssam		}
333178354Ssam		if (mt->netband->maxPower == 0) {
334186100Ssam			warnx("no maxpower for band at line %ld",
335186100Ssam			   XML_GetCurrentLineNumber(mt->parser));
336178354Ssam		}
337178354Ssam		/* default max power w/ DFS to max power */
338178354Ssam		if (mt->netband->maxPowerDFS == 0)
339178354Ssam			mt->netband->maxPowerDFS = mt->netband->maxPower;
340178354Ssam		mt->netband = NULL;
341178354Ssam		goto done;
342178354Ssam	}
343178354Ssam	/* </netband> */
344178354Ssam	if (iseq(name, "netband") && mt->netband != NULL) {
345178354Ssam		mt->curband = NULL;
346178354Ssam		goto done;
347178354Ssam	}
348178354Ssam	/* </country> */
349178354Ssam	if (iseq(name, "country") && mt->country != NULL) {
350186100Ssam		if (mt->country->code == NO_COUNTRY) {
351186100Ssam			warnx("no ISO cc for country at line %ld",
352186100Ssam			   XML_GetCurrentLineNumber(mt->parser));
353178354Ssam		}
354178354Ssam		if (mt->country->name == NULL) {
355186100Ssam			warnx("no name for country at line %ld",
356186100Ssam			   XML_GetCurrentLineNumber(mt->parser));
357178354Ssam		}
358178354Ssam		if (mt->country->rd == NULL) {
359186100Ssam			warnx("no regdomain reference for country at line %ld",
360186100Ssam			   XML_GetCurrentLineNumber(mt->parser));
361178354Ssam		}
362178354Ssam		mt->country = NULL;
363178354Ssam		goto done;
364178354Ssam	}
365178354Ssamdone:
366178354Ssam	sbuf_delete(mt->sbuf[mt->level]);
367178354Ssam	mt->sbuf[mt->level--] = NULL;
368178354Ssam#undef iseq
369178354Ssam}
370178354Ssam
371178354Ssamstatic void
372178354Ssamchar_data(void *data, const XML_Char *s, int len)
373178354Ssam{
374178354Ssam	struct mystate *mt;
375178354Ssam	const char *b, *e;
376178354Ssam
377178354Ssam	mt = data;
378178354Ssam
379178354Ssam	b = s;
380178354Ssam	e = s + len-1;
381178354Ssam	for (; isspace(*b) && b < e; b++)
382178354Ssam		;
383178354Ssam	for (; isspace(*e) && e > b; e++)
384178354Ssam		;
385178354Ssam	if (e != b || (*b != '\0' && !isspace(*b)))
386178354Ssam		sbuf_bcat(mt->sbuf[mt->level], b, e-b+1);
387178354Ssam}
388178354Ssam
389178354Ssamstatic void *
390178354Ssamfindid(struct regdata *rdp, const void *id, int type)
391178354Ssam{
392178354Ssam	struct ident *ip;
393178354Ssam
394178354Ssam	for (ip = rdp->ident; ip->id != NULL; ip++)
395178354Ssam		if (ip->type == type && strcasecmp(ip->id, id) == 0)
396178354Ssam			return ip->p;
397178354Ssam	return NULL;
398178354Ssam}
399178354Ssam
400178354Ssam/*
401178354Ssam * Parse an regdomain XML configuration and build the internal representation.
402178354Ssam */
403178354Ssamint
404178354Ssamlib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len)
405178354Ssam{
406178354Ssam	struct mystate *mt;
407178354Ssam	struct regdomain *dp;
408178354Ssam	struct country *cp;
409178354Ssam	struct freqband *fp;
410178354Ssam	struct netband *nb;
411178354Ssam	const void *id;
412183244Ssam	int i, errors;
413178354Ssam
414178354Ssam	memset(rdp, 0, sizeof(struct regdata));
415178354Ssam	mt = calloc(1, sizeof(struct mystate));
416178354Ssam	if (mt == NULL)
417178354Ssam		return ENOMEM;
418178354Ssam	/* parse the XML input */
419178354Ssam	mt->rdp = rdp;
420186100Ssam	mt->parser = XML_ParserCreate(NULL);
421186100Ssam	XML_SetUserData(mt->parser, mt);
422186100Ssam	XML_SetElementHandler(mt->parser, start_element, end_element);
423186100Ssam	XML_SetCharacterDataHandler(mt->parser, char_data);
424186100Ssam	if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) {
425178852Scokane		warnx("%s: %s at line %ld", __func__,
426186100Ssam		   XML_ErrorString(XML_GetErrorCode(mt->parser)),
427186100Ssam		   XML_GetCurrentLineNumber(mt->parser));
428178354Ssam		return -1;
429178354Ssam	}
430186100Ssam	XML_ParserFree(mt->parser);
431178354Ssam
432178354Ssam	/* setup the identifer table */
433178354Ssam	rdp->ident = calloc(sizeof(struct ident), mt->nident + 1);
434178354Ssam	if (rdp->ident == NULL)
435178354Ssam		return ENOMEM;
436178354Ssam	free(mt);
437183244Ssam
438183244Ssam	errors = 0;
439178354Ssam	i = 0;
440178354Ssam	LIST_FOREACH(dp, &rdp->domains, next) {
441178354Ssam		rdp->ident[i].id = dp->name;
442178354Ssam		rdp->ident[i].p = dp;
443178354Ssam		rdp->ident[i].type = DOMAIN;
444178354Ssam		i++;
445178354Ssam	}
446178354Ssam	LIST_FOREACH(fp, &rdp->freqbands, next) {
447178354Ssam		rdp->ident[i].id = fp->id;
448178354Ssam		rdp->ident[i].p = fp;
449178354Ssam		rdp->ident[i].type = FREQBAND;
450178354Ssam		i++;
451178354Ssam	}
452178354Ssam	LIST_FOREACH(cp, &rdp->countries, next) {
453178354Ssam		rdp->ident[i].id = cp->isoname;
454178354Ssam		rdp->ident[i].p = cp;
455178354Ssam		rdp->ident[i].type = COUNTRY;
456178354Ssam		i++;
457178354Ssam	}
458178354Ssam
459178354Ssam	/* patch references */
460178354Ssam	LIST_FOREACH(dp, &rdp->domains, next) {
461178354Ssam		if (dp->cc != NULL) {
462178354Ssam			id = dp->cc;
463178354Ssam			dp->cc = findid(rdp, id, COUNTRY);
464183244Ssam			if (dp->cc == NULL) {
465183244Ssam				warnx("undefined country \"%s\"",
466183244Ssam				    __DECONST(char *, id));
467183244Ssam				errors++;
468183244Ssam			}
469178354Ssam			free(__DECONST(char *, id));
470178354Ssam		}
471183244Ssam		LIST_FOREACH(nb, &dp->bands_11b, next) {
472183244Ssam			id = findid(rdp, nb->band, FREQBAND);
473183244Ssam			if (id == NULL) {
474183244Ssam				warnx("undefined 11b band \"%s\"",
475183244Ssam				    __DECONST(char *, nb->band));
476183244Ssam				errors++;
477183244Ssam			}
478183244Ssam			nb->band = id;
479183244Ssam		}
480183244Ssam		LIST_FOREACH(nb, &dp->bands_11g, next) {
481183244Ssam			id = findid(rdp, nb->band, FREQBAND);
482183244Ssam			if (id == NULL) {
483183244Ssam				warnx("undefined 11g band \"%s\"",
484183244Ssam				    __DECONST(char *, nb->band));
485183244Ssam				errors++;
486183244Ssam			}
487183244Ssam			nb->band = id;
488183244Ssam		}
489183244Ssam		LIST_FOREACH(nb, &dp->bands_11a, next) {
490183244Ssam			id = findid(rdp, nb->band, FREQBAND);
491183244Ssam			if (id == NULL) {
492183244Ssam				warnx("undefined 11a band \"%s\"",
493183244Ssam				    __DECONST(char *, nb->band));
494183244Ssam				errors++;
495183244Ssam			}
496183244Ssam			nb->band = id;
497183244Ssam		}
498183244Ssam		LIST_FOREACH(nb, &dp->bands_11ng, next) {
499183244Ssam			id = findid(rdp, nb->band, FREQBAND);
500183244Ssam			if (id == NULL) {
501183244Ssam				warnx("undefined 11ng band \"%s\"",
502183244Ssam				    __DECONST(char *, nb->band));
503183244Ssam				errors++;
504183244Ssam			}
505183244Ssam			nb->band = id;
506183244Ssam		}
507183244Ssam		LIST_FOREACH(nb, &dp->bands_11na, next) {
508183244Ssam			id = findid(rdp, nb->band, FREQBAND);
509183244Ssam			if (id == NULL) {
510183244Ssam				warnx("undefined 11na band \"%s\"",
511183244Ssam				    __DECONST(char *, nb->band));
512183244Ssam				errors++;
513183244Ssam			}
514183244Ssam			nb->band = id;
515183244Ssam		}
516178354Ssam	}
517178354Ssam	LIST_FOREACH(cp, &rdp->countries, next) {
518178354Ssam		id = cp->rd;
519178354Ssam		cp->rd = findid(rdp, id, DOMAIN);
520183244Ssam		if (cp->rd == NULL) {
521183244Ssam			warnx("undefined country \"%s\"",
522183244Ssam			    __DECONST(char *, id));
523183244Ssam			errors++;
524183244Ssam		}
525178354Ssam		free(__DECONST(char *, id));
526178354Ssam	}
527178354Ssam
528183244Ssam	return errors ? EINVAL : 0;
529178354Ssam}
530178354Ssam
531178354Ssamstatic void
532178354Ssamcleanup_bands(netband_head *head)
533178354Ssam{
534178354Ssam	struct netband *nb;
535178354Ssam
536178354Ssam	for (;;) {
537178354Ssam		nb = LIST_FIRST(head);
538178354Ssam		if (nb == NULL)
539178354Ssam			break;
540178354Ssam		free(nb);
541178354Ssam	}
542178354Ssam}
543178354Ssam
544178354Ssam/*
545178354Ssam * Cleanup state/resources for a previously parsed regdomain database.
546178354Ssam */
547178354Ssamvoid
548178354Ssamlib80211_regdomain_cleanup(struct regdata *rdp)
549178354Ssam{
550178354Ssam
551178354Ssam	free(rdp->ident);
552178354Ssam	rdp->ident = NULL;
553178354Ssam	for (;;) {
554178354Ssam		struct regdomain *dp = LIST_FIRST(&rdp->domains);
555178354Ssam		if (dp == NULL)
556178354Ssam			break;
557178354Ssam		LIST_REMOVE(dp, next);
558178354Ssam		cleanup_bands(&dp->bands_11b);
559178354Ssam		cleanup_bands(&dp->bands_11g);
560178354Ssam		cleanup_bands(&dp->bands_11a);
561178354Ssam		cleanup_bands(&dp->bands_11ng);
562178354Ssam		cleanup_bands(&dp->bands_11na);
563178354Ssam		if (dp->name != NULL)
564178354Ssam			free(__DECONST(char *, dp->name));
565178354Ssam	}
566178354Ssam	for (;;) {
567178354Ssam		struct country *cp = LIST_FIRST(&rdp->countries);
568178354Ssam		if (cp == NULL)
569178354Ssam			break;
570178354Ssam		LIST_REMOVE(cp, next);
571178354Ssam		if (cp->name != NULL)
572178354Ssam			free(__DECONST(char *, cp->name));
573178354Ssam		free(cp);
574178354Ssam	}
575178354Ssam	for (;;) {
576178354Ssam		struct freqband *fp = LIST_FIRST(&rdp->freqbands);
577178354Ssam		if (fp == NULL)
578178354Ssam			break;
579178354Ssam		LIST_REMOVE(fp, next);
580178354Ssam		free(fp);
581178354Ssam	}
582178354Ssam}
583178354Ssam
584178354Ssamstruct regdata *
585178354Ssamlib80211_alloc_regdata(void)
586178354Ssam{
587178354Ssam	struct regdata *rdp;
588178354Ssam	struct stat sb;
589178354Ssam	void *xml;
590178354Ssam	int fd;
591178354Ssam
592178354Ssam	rdp = calloc(1, sizeof(struct regdata));
593178354Ssam
594178354Ssam	fd = open(_PATH_REGDOMAIN, O_RDONLY);
595178354Ssam	if (fd < 0) {
596178354Ssam#ifdef DEBUG
597178354Ssam		warn("%s: open(%s)", __func__, _PATH_REGDOMAIN);
598178354Ssam#endif
599178354Ssam		free(rdp);
600178354Ssam		return NULL;
601178354Ssam	}
602178354Ssam	if (fstat(fd, &sb) < 0) {
603178354Ssam#ifdef DEBUG
604178354Ssam		warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN);
605178354Ssam#endif
606178354Ssam		close(fd);
607178354Ssam		free(rdp);
608178354Ssam		return NULL;
609178354Ssam	}
610178354Ssam	xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
611178354Ssam	if (xml == MAP_FAILED) {
612178354Ssam#ifdef DEBUG
613178354Ssam		warn("%s: mmap", __func__);
614178354Ssam#endif
615178354Ssam		close(fd);
616178354Ssam		free(rdp);
617178354Ssam		return NULL;
618178354Ssam	}
619178354Ssam	if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) {
620178354Ssam#ifdef DEBUG
621178354Ssam		warn("%s: error reading regulatory database", __func__);
622178354Ssam#endif
623178354Ssam		munmap(xml, sb.st_size);
624178354Ssam		close(fd);
625178354Ssam		free(rdp);
626178354Ssam		return NULL;
627178354Ssam	}
628178354Ssam	munmap(xml, sb.st_size);
629178354Ssam	close(fd);
630178354Ssam
631178354Ssam	return rdp;
632178354Ssam}
633178354Ssam
634178354Ssamvoid
635178354Ssamlib80211_free_regdata(struct regdata *rdp)
636178354Ssam{
637178354Ssam	lib80211_regdomain_cleanup(rdp);
638178354Ssam	free(rdp);
639178354Ssam}
640178354Ssam
641178354Ssam/*
642178354Ssam * Lookup a regdomain by SKU.
643178354Ssam */
644178354Ssamconst struct regdomain *
645178354Ssamlib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku)
646178354Ssam{
647178354Ssam	const struct regdomain *dp;
648178354Ssam
649178354Ssam	LIST_FOREACH(dp, &rdp->domains, next) {
650178354Ssam		if (dp->sku == sku)
651178354Ssam			return dp;
652178354Ssam	}
653178354Ssam	return NULL;
654178354Ssam}
655178354Ssam
656178354Ssam/*
657178354Ssam * Lookup a regdomain by name.
658178354Ssam */
659178354Ssamconst struct regdomain *
660178354Ssamlib80211_regdomain_findbyname(const struct regdata *rdp, const char *name)
661178354Ssam{
662178354Ssam	const struct regdomain *dp;
663178354Ssam
664178354Ssam	LIST_FOREACH(dp, &rdp->domains, next) {
665178354Ssam		if (strcasecmp(dp->name, name) == 0)
666178354Ssam			return dp;
667178354Ssam	}
668178354Ssam	return NULL;
669178354Ssam}
670178354Ssam
671178354Ssam/*
672178354Ssam * Lookup a country by ISO country code.
673178354Ssam */
674178354Ssamconst struct country *
675178354Ssamlib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc)
676178354Ssam{
677178354Ssam	const struct country *cp;
678178354Ssam
679178354Ssam	LIST_FOREACH(cp, &rdp->countries, next) {
680178354Ssam		if (cp->code == cc)
681178354Ssam			return cp;
682178354Ssam	}
683178354Ssam	return NULL;
684178354Ssam}
685178354Ssam
686178354Ssam/*
687178354Ssam * Lookup a country by ISO/long name.
688178354Ssam */
689178354Ssamconst struct country *
690178354Ssamlib80211_country_findbyname(const struct regdata *rdp, const char *name)
691178354Ssam{
692178354Ssam	const struct country *cp;
693178354Ssam	int len;
694178354Ssam
695178354Ssam	len = strlen(name);
696178354Ssam	LIST_FOREACH(cp, &rdp->countries, next) {
697200587Sgavin		if (strcasecmp(cp->isoname, name) == 0)
698178354Ssam			return cp;
699178354Ssam	}
700200587Sgavin	LIST_FOREACH(cp, &rdp->countries, next) {
701200587Sgavin		if (strncasecmp(cp->name, name, len) == 0)
702200587Sgavin			return cp;
703200587Sgavin	}
704178354Ssam	return NULL;
705178354Ssam}
706