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