2 * Copyright (c) 2008 Sam Leffler, Errno Consulting
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
25 * $FreeBSD: head/sbin/ifconfig/regdomain.c 200587 2009-12-15 20:44:12Z gavin $
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/errno.h>
44 #include "regdomain.h"
46 #include <netproto/802_11/_ieee80211.h>
53 struct regdomain *rd; /* current domain */
54 struct netband *netband; /* current netband */
55 struct freqband *freqband; /* current freqband */
56 struct country *country; /* current country */
57 netband_head *curband; /* current netband list */
59 struct sbuf *sbuf[MAXLEVEL];
64 iseq(const char *a, const char *b)
66 return (strcasecmp(a, b) == 0);
70 start_element(void *data, const char *name, const char **attr)
72 static netband_head bands_unknown; /* marker for unknown netband */
74 const void *id, *ref, *mode;
78 if (++mt->level == MAXLEVEL) {
79 /* XXX force parser to abort */
82 mt->sbuf[mt->level] = sbuf_new_auto();
83 id = ref = mode = NULL;
84 for (i = 0; attr[i] != NULL; i += 2) {
85 if (iseq(attr[i], "id")) {
87 } else if (iseq(attr[i], "ref")) {
89 } else if (iseq(attr[i], "mode")) {
92 printf("%*.*s[%s = %s]\n", mt->level + 1,
93 mt->level + 1, "", attr[i], attr[i+1]);
95 if (iseq(name, "rd") && mt->rd == NULL) {
96 if (mt->country == NULL) {
97 mt->rd = calloc(1, sizeof(struct regdomain));
98 mt->rd->name = strdup(id);
100 LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
102 mt->country->rd = (void *)strdup(ref);
105 if (iseq(name, "defcc") && mt->rd != NULL) {
106 mt->rd->cc = (void *)strdup(ref);
109 if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
111 warnx("no mode for netband at line %ld",
112 XML_GetCurrentLineNumber(mt->parser));
115 if (iseq(mode, "11b"))
116 mt->curband = &mt->rd->bands_11b;
117 else if (iseq(mode, "11g"))
118 mt->curband = &mt->rd->bands_11g;
119 else if (iseq(mode, "11a"))
120 mt->curband = &mt->rd->bands_11a;
121 else if (iseq(mode, "11ng"))
122 mt->curband = &mt->rd->bands_11ng;
123 else if (iseq(mode, "11na"))
124 mt->curband = &mt->rd->bands_11na;
125 else if (iseq(mode, "11ac"))
126 mt->curband = &mt->rd->bands_11ac;
127 else if (iseq(mode, "11acg"))
128 mt->curband = &mt->rd->bands_11acg;
130 mt->curband = &bands_unknown;
131 warnx("unknown netband mode \"%s\" at line %ld",
132 __DECONST(char *, mode),
133 XML_GetCurrentLineNumber(mt->parser));
137 if (mt->curband == &bands_unknown) {
138 warnx("ignore \"%s\" of unknown netband at line %ld",
139 name, XML_GetCurrentLineNumber(mt->parser));
142 if (iseq(name, "band") && mt->netband == NULL) {
143 if (mt->curband == NULL) {
144 warnx("band without enclosing netband at line %ld",
145 XML_GetCurrentLineNumber(mt->parser));
148 mt->netband = calloc(1, sizeof(struct netband));
149 LIST_INSERT_HEAD(mt->curband, mt->netband, next);
152 if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) {
153 /* XXX handle inlines and merge into table? */
154 if (mt->netband->band != NULL) {
155 warnx("duplicate freqband at line %ld ignored",
156 XML_GetCurrentLineNumber(mt->parser));
159 mt->netband->band = (void *)strdup(ref);
163 if (iseq(name, "country") && mt->country == NULL) {
164 mt->country = calloc(1, sizeof(struct country));
165 mt->country->isoname = strdup(id);
166 mt->country->code = NO_COUNTRY;
168 LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
172 if (iseq(name, "freqband") && mt->freqband == NULL) {
173 mt->freqband = calloc(1, sizeof(struct freqband));
174 mt->freqband->id = strdup(id);
176 LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
182 decode_flag(struct mystate *mt, const char *p, int len)
184 static const struct {
189 #define FLAG(x) { #x, sizeof(#x)-1, x }
190 FLAG(IEEE80211_CHAN_A),
191 FLAG(IEEE80211_CHAN_B),
192 FLAG(IEEE80211_CHAN_G),
193 FLAG(IEEE80211_CHAN_HT20),
194 FLAG(IEEE80211_CHAN_HT40),
195 FLAG(IEEE80211_CHAN_VHT20),
196 FLAG(IEEE80211_CHAN_VHT40),
197 FLAG(IEEE80211_CHAN_VHT80),
198 FLAG(IEEE80211_CHAN_VHT160),
200 * XXX VHT80P80? This likely should be done by
201 * 80MHz chan logic in net80211 / ifconfig.
203 FLAG(IEEE80211_CHAN_ST),
204 FLAG(IEEE80211_CHAN_TURBO),
205 FLAG(IEEE80211_CHAN_PASSIVE),
206 FLAG(IEEE80211_CHAN_DFS),
207 FLAG(IEEE80211_CHAN_CCK),
208 FLAG(IEEE80211_CHAN_OFDM),
209 FLAG(IEEE80211_CHAN_2GHZ),
210 FLAG(IEEE80211_CHAN_5GHZ),
211 FLAG(IEEE80211_CHAN_DYN),
212 FLAG(IEEE80211_CHAN_GFSK),
213 FLAG(IEEE80211_CHAN_GSM),
214 FLAG(IEEE80211_CHAN_STURBO),
215 FLAG(IEEE80211_CHAN_HALF),
216 FLAG(IEEE80211_CHAN_QUARTER),
217 FLAG(IEEE80211_CHAN_HT40U),
218 FLAG(IEEE80211_CHAN_HT40D),
219 FLAG(IEEE80211_CHAN_4MSXMIT),
220 FLAG(IEEE80211_CHAN_NOADHOC),
221 FLAG(IEEE80211_CHAN_NOHOSTAP),
222 FLAG(IEEE80211_CHAN_11D),
223 FLAG(IEEE80211_CHAN_FHSS),
224 FLAG(IEEE80211_CHAN_PUREG),
225 FLAG(IEEE80211_CHAN_108A),
226 FLAG(IEEE80211_CHAN_108G),
228 { "ECM", 3, REQ_ECM },
229 { "INDOOR", 6, REQ_INDOOR },
230 { "OUTDOOR", 7, REQ_OUTDOOR },
234 for (i = 0; i < nitems(flags); i++)
235 if (len == flags[i].len && iseq(p, flags[i].name))
236 return flags[i].value;
237 warnx("unknown flag \"%.*s\" at line %ld ignored",
238 len, p, XML_GetCurrentLineNumber(mt->parser));
243 end_element(void *data, const char *name)
250 sbuf_finish(mt->sbuf[mt->level]);
251 p = sbuf_data(mt->sbuf[mt->level]);
252 len = sbuf_len(mt->sbuf[mt->level]);
254 /* <freqband>...</freqband> */
255 if (iseq(name, "freqstart") && mt->freqband != NULL) {
256 mt->freqband->freqStart = strtoul(p, NULL, 0);
259 if (iseq(name, "freqend") && mt->freqband != NULL) {
260 mt->freqband->freqEnd = strtoul(p, NULL, 0);
263 if (iseq(name, "chanwidth") && mt->freqband != NULL) {
264 mt->freqband->chanWidth = strtoul(p, NULL, 0);
267 if (iseq(name, "chansep") && mt->freqband != NULL) {
268 mt->freqband->chanSep = strtoul(p, NULL, 0);
271 if (iseq(name, "flags")) {
272 if (mt->freqband != NULL)
273 mt->freqband->flags |= decode_flag(mt, p, len);
274 else if (mt->netband != NULL)
275 mt->netband->flags |= decode_flag(mt, p, len);
277 warnx("flags without freqband or netband at line %ld ignored",
278 XML_GetCurrentLineNumber(mt->parser));
284 if (iseq(name, "name") && mt->rd != NULL) {
285 mt->rd->name = strdup(p);
288 if (iseq(name, "sku") && mt->rd != NULL) {
289 mt->rd->sku = strtoul(p, NULL, 0);
292 if (iseq(name, "netband") && mt->rd != NULL) {
297 /* <band> ... </band> */
298 if (iseq(name, "freqband") && mt->netband != NULL) {
299 /* XXX handle inline freqbands */
302 if (iseq(name, "maxpower") && mt->netband != NULL) {
303 mt->netband->maxPower = strtoul(p, NULL, 0);
306 if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
307 mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
310 if (iseq(name, "maxantgain") && mt->netband != NULL) {
311 mt->netband->maxAntGain = strtoul(p, NULL, 0);
315 /* <country>...</country> */
316 if (iseq(name, "isocc") && mt->country != NULL) {
317 mt->country->code = strtoul(p, NULL, 0);
320 if (iseq(name, "name") && mt->country != NULL) {
321 mt->country->name = strdup(p);
326 warnx("unexpected XML token \"%s\" data \"%s\" at line %ld",
327 name, p, XML_GetCurrentLineNumber(mt->parser));
331 if (iseq(name, "freqband") && mt->freqband != NULL) {
332 /* XXX must have start/end frequencies */
333 /* XXX must have channel width/sep */
338 if (iseq(name, "rd") && mt->rd != NULL) {
343 if (iseq(name, "band") && mt->netband != NULL) {
344 if (mt->netband->band == NULL) {
345 warnx("no freqbands for band at line %ld",
346 XML_GetCurrentLineNumber(mt->parser));
348 if (mt->netband->maxPower == 0) {
349 warnx("no maxpower for band at line %ld",
350 XML_GetCurrentLineNumber(mt->parser));
352 /* default max power w/ DFS to max power */
353 if (mt->netband->maxPowerDFS == 0)
354 mt->netband->maxPowerDFS = mt->netband->maxPower;
359 if (iseq(name, "netband") && mt->netband != NULL) {
364 if (iseq(name, "country") && mt->country != NULL) {
365 if (mt->country->code == NO_COUNTRY) {
366 warnx("no ISO cc for country at line %ld",
367 XML_GetCurrentLineNumber(mt->parser));
369 if (mt->country->name == NULL) {
370 warnx("no name for country at line %ld",
371 XML_GetCurrentLineNumber(mt->parser));
373 if (mt->country->rd == NULL) {
374 warnx("no regdomain reference for country at line %ld",
375 XML_GetCurrentLineNumber(mt->parser));
381 sbuf_delete(mt->sbuf[mt->level]);
382 mt->sbuf[mt->level--] = NULL;
386 char_data(void *data, const XML_Char *s, int len)
395 for (; isspace(*b) && b < e; b++)
397 for (; isspace(*e) && e > b; e++)
399 if (e != b || (*b != '\0' && !isspace(*b)))
400 sbuf_bcat(mt->sbuf[mt->level], b, e-b+1);
404 findid(struct regdata *rdp, const void *id, enum IdentType type)
408 for (ip = rdp->ident; ip->id != NULL; ip++)
409 if (ip->type == type && iseq(ip->id, id))
415 * Parse an regdomain XML configuration and build the internal representation.
418 lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len)
421 struct regdomain *dp;
428 memset(rdp, 0, sizeof(struct regdata));
429 mt = calloc(1, sizeof(struct mystate));
432 /* parse the XML input */
434 mt->parser = XML_ParserCreate(NULL);
435 XML_SetUserData(mt->parser, mt);
436 XML_SetElementHandler(mt->parser, start_element, end_element);
437 XML_SetCharacterDataHandler(mt->parser, char_data);
438 if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) {
439 warnx("%s: %s at line %ld", __func__,
440 XML_ErrorString(XML_GetErrorCode(mt->parser)),
441 XML_GetCurrentLineNumber(mt->parser));
444 XML_ParserFree(mt->parser);
446 /* setup the identifer table */
447 rdp->ident = calloc(sizeof(struct ident), mt->nident + 1);
448 if (rdp->ident == NULL)
454 LIST_FOREACH(dp, &rdp->domains, next) {
455 rdp->ident[i].id = dp->name;
456 rdp->ident[i].p = dp;
457 rdp->ident[i].type = DOMAIN;
460 LIST_FOREACH(fp, &rdp->freqbands, next) {
461 rdp->ident[i].id = fp->id;
462 rdp->ident[i].p = fp;
463 rdp->ident[i].type = FREQBAND;
466 LIST_FOREACH(cp, &rdp->countries, next) {
467 rdp->ident[i].id = cp->isoname;
468 rdp->ident[i].p = cp;
469 rdp->ident[i].type = COUNTRY;
473 /* patch references */
474 LIST_FOREACH(dp, &rdp->domains, next) {
475 if (dp->cc != NULL) {
477 dp->cc = findid(rdp, id, COUNTRY);
478 if (dp->cc == NULL) {
479 warnx("undefined country \"%s\"",
480 __DECONST(char *, id));
483 free(__DECONST(char *, id));
485 LIST_FOREACH(nb, &dp->bands_11b, next) {
486 id = findid(rdp, nb->band, FREQBAND);
488 warnx("undefined 11b band \"%s\"",
489 __DECONST(char *, nb->band));
494 LIST_FOREACH(nb, &dp->bands_11g, next) {
495 id = findid(rdp, nb->band, FREQBAND);
497 warnx("undefined 11g band \"%s\"",
498 __DECONST(char *, nb->band));
503 LIST_FOREACH(nb, &dp->bands_11a, next) {
504 id = findid(rdp, nb->band, FREQBAND);
506 warnx("undefined 11a band \"%s\"",
507 __DECONST(char *, nb->band));
512 LIST_FOREACH(nb, &dp->bands_11ng, next) {
513 id = findid(rdp, nb->band, FREQBAND);
515 warnx("undefined 11ng band \"%s\"",
516 __DECONST(char *, nb->band));
521 LIST_FOREACH(nb, &dp->bands_11na, next) {
522 id = findid(rdp, nb->band, FREQBAND);
524 warnx("undefined 11na band \"%s\"",
525 __DECONST(char *, nb->band));
530 LIST_FOREACH(nb, &dp->bands_11ac, next) {
531 id = findid(rdp, nb->band, FREQBAND);
533 warnx("undefined 11ac band \"%s\"",
534 __DECONST(char *, nb->band));
539 LIST_FOREACH(nb, &dp->bands_11acg, next) {
540 id = findid(rdp, nb->band, FREQBAND);
542 warnx("undefined 11acg band \"%s\"",
543 __DECONST(char *, nb->band));
549 LIST_FOREACH(cp, &rdp->countries, next) {
551 cp->rd = findid(rdp, id, DOMAIN);
552 if (cp->rd == NULL) {
553 warnx("undefined country \"%s\"",
554 __DECONST(char *, id));
557 free(__DECONST(char *, id));
560 return errors ? EINVAL : 0;
564 cleanup_bands(netband_head *head)
569 nb = LIST_FIRST(head);
572 LIST_REMOVE(nb, next);
578 * Cleanup state/resources for a previously parsed regdomain database.
581 lib80211_regdomain_cleanup(struct regdata *rdp)
587 struct regdomain *dp = LIST_FIRST(&rdp->domains);
590 LIST_REMOVE(dp, next);
591 cleanup_bands(&dp->bands_11b);
592 cleanup_bands(&dp->bands_11g);
593 cleanup_bands(&dp->bands_11a);
594 cleanup_bands(&dp->bands_11ng);
595 cleanup_bands(&dp->bands_11na);
596 cleanup_bands(&dp->bands_11ac);
597 cleanup_bands(&dp->bands_11acg);
598 if (dp->name != NULL)
599 free(__DECONST(char *, dp->name));
602 struct country *cp = LIST_FIRST(&rdp->countries);
605 LIST_REMOVE(cp, next);
606 if (cp->name != NULL)
607 free(__DECONST(char *, cp->name));
611 struct freqband *fp = LIST_FIRST(&rdp->freqbands);
614 LIST_REMOVE(fp, next);
620 lib80211_alloc_regdata(void)
627 rdp = calloc(1, sizeof(struct regdata));
629 fd = open(_PATH_REGDOMAIN, O_RDONLY);
632 warn("%s: open(%s)", __func__, _PATH_REGDOMAIN);
637 if (fstat(fd, &sb) < 0) {
639 warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN);
645 xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
646 if (xml == MAP_FAILED) {
648 warn("%s: mmap", __func__);
654 if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) {
656 warn("%s: error reading regulatory database", __func__);
658 munmap(xml, sb.st_size);
663 munmap(xml, sb.st_size);
670 lib80211_free_regdata(struct regdata *rdp)
672 lib80211_regdomain_cleanup(rdp);
677 * Lookup a regdomain by SKU.
679 const struct regdomain *
680 lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku)
682 const struct regdomain *dp;
684 LIST_FOREACH(dp, &rdp->domains, next) {
692 * Lookup a regdomain by name.
694 const struct regdomain *
695 lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name)
697 const struct regdomain *dp;
699 LIST_FOREACH(dp, &rdp->domains, next) {
700 if (iseq(dp->name, name))
707 * Lookup a country by ISO country code.
709 const struct country *
710 lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc)
712 const struct country *cp;
714 LIST_FOREACH(cp, &rdp->countries, next) {
722 * Lookup a country by ISO/long name.
724 const struct country *
725 lib80211_country_findbyname(const struct regdata *rdp, const char *name)
727 const struct country *cp;
730 LIST_FOREACH(cp, &rdp->countries, next) {
731 if (iseq(cp->isoname, name))
735 LIST_FOREACH(cp, &rdp->countries, next) {
736 if (strncasecmp(cp->name, name, len) == 0)