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