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