xref: /dragonfly/sbin/ifconfig/regdomain.c (revision f1699cf2fc932a411e565782d09c34554ceeaa40)
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  * $FreeBSD: head/sbin/ifconfig/regdomain.c 200587 2009-12-15 20:44:12Z gavin $
26  */
27 
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/errno.h>
31 #include <sys/mman.h>
32 #include <sys/sbuf.h>
33 #include <sys/stat.h>
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <fcntl.h>
39 #include <err.h>
40 #include <unistd.h>
41 
42 #include <bsdxml.h>
43 
44 #include "regdomain.h"
45 
46 #include <netproto/802_11/_ieee80211.h>
47 
48 #define   MAXLEVEL  20
49 
50 struct mystate {
51           XML_Parser                    parser;
52           struct regdata                *rdp;
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 */
58           int                           level;
59           struct sbuf                   *sbuf[MAXLEVEL];
60           int                           nident;
61 };
62 
63 static int
iseq(const char * a,const char * b)64 iseq(const char *a, const char *b)
65 {
66           return (strcasecmp(a, b) == 0);
67 }
68 
69 static void
start_element(void * data,const char * name,const char ** attr)70 start_element(void *data, const char *name, const char **attr)
71 {
72           static netband_head bands_unknown;  /* marker for unknown netband */
73           struct mystate *mt;
74           const void *id, *ref, *mode;
75           int i;
76 
77           mt = data;
78           if (++mt->level == MAXLEVEL) {
79                     /* XXX force parser to abort */
80                     return;
81           }
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")) {
86                               id = attr[i+1];
87                     } else if (iseq(attr[i], "ref")) {
88                               ref = attr[i+1];
89                     } else if (iseq(attr[i], "mode")) {
90                               mode = attr[i+1];
91                     } else
92                               printf("%*.*s[%s = %s]\n", mt->level + 1,
93                                   mt->level + 1, "", attr[i], attr[i+1]);
94           }
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);
99                               mt->nident++;
100                               LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
101                     } else
102                               mt->country->rd = (void *)strdup(ref);
103                     return;
104           }
105           if (iseq(name, "defcc") && mt->rd != NULL) {
106                     mt->rd->cc = (void *)strdup(ref);
107                     return;
108           }
109           if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
110                     if (mode == NULL) {
111                               warnx("no mode for netband at line %ld",
112                                   XML_GetCurrentLineNumber(mt->parser));
113                               return;
114                     }
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;
129                     else {
130                               mt->curband = &bands_unknown;
131                               warnx("unknown netband mode \"%s\" at line %ld",
132                                   __DECONST(char *, mode),
133                                   XML_GetCurrentLineNumber(mt->parser));
134                     }
135                     return;
136           }
137           if (mt->curband == &bands_unknown) {
138                     warnx("ignore \"%s\" of unknown netband at line %ld",
139                         name, XML_GetCurrentLineNumber(mt->parser));
140                     return;
141           }
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));
146                               return;
147                     }
148                     mt->netband = calloc(1, sizeof(struct netband));
149                     LIST_INSERT_HEAD(mt->curband, mt->netband, next);
150                     return;
151           }
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));
157                               /* XXX complain */
158                     } else
159                               mt->netband->band = (void *)strdup(ref);
160                     return;
161           }
162 
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;
167                     mt->nident++;
168                     LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
169                     return;
170           }
171 
172           if (iseq(name, "freqband") && mt->freqband == NULL) {
173                     mt->freqband = calloc(1, sizeof(struct freqband));
174                     mt->freqband->id = strdup(id);
175                     mt->nident++;
176                     LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
177                     return;
178           }
179 }
180 
181 static int
decode_flag(struct mystate * mt,const char * p,int len)182 decode_flag(struct mystate *mt, const char *p, int len)
183 {
184           static const struct {
185                     const char *name;
186                     int len;
187                     uint32_t value;
188           } flags[] = {
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),
199                     /*
200                      * XXX VHT80P80? This likely should be done by
201                      * 80MHz chan logic in net80211 / ifconfig.
202                      */
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),
227 #undef FLAG
228                     { "ECM",  3,        REQ_ECM },
229                     { "INDOOR",         6,        REQ_INDOOR },
230                     { "OUTDOOR",        7,        REQ_OUTDOOR },
231           };
232           size_t i;
233 
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));
239           return 0;
240 }
241 
242 static void
end_element(void * data,const char * name)243 end_element(void *data, const char *name)
244 {
245           struct mystate *mt;
246           int len;
247           char *p;
248 
249           mt = data;
250           sbuf_finish(mt->sbuf[mt->level]);
251           p = sbuf_data(mt->sbuf[mt->level]);
252           len = sbuf_len(mt->sbuf[mt->level]);
253 
254           /* <freqband>...</freqband> */
255           if (iseq(name, "freqstart") && mt->freqband != NULL) {
256                     mt->freqband->freqStart = strtoul(p, NULL, 0);
257                     goto done;
258           }
259           if (iseq(name, "freqend") && mt->freqband != NULL) {
260                     mt->freqband->freqEnd = strtoul(p, NULL, 0);
261                     goto done;
262           }
263           if (iseq(name, "chanwidth") && mt->freqband != NULL) {
264                     mt->freqband->chanWidth = strtoul(p, NULL, 0);
265                     goto done;
266           }
267           if (iseq(name, "chansep") && mt->freqband != NULL) {
268                     mt->freqband->chanSep = strtoul(p, NULL, 0);
269                     goto done;
270           }
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);
276                     else {
277                               warnx("flags without freqband or netband at line %ld ignored",
278                                   XML_GetCurrentLineNumber(mt->parser));
279                     }
280                     goto done;
281           }
282 
283           /* <rd> ... </rd> */
284           if (iseq(name, "name") && mt->rd != NULL) {
285                     mt->rd->name = strdup(p);
286                     goto done;
287           }
288           if (iseq(name, "sku") && mt->rd != NULL) {
289                     mt->rd->sku = strtoul(p, NULL, 0);
290                     goto done;
291           }
292           if (iseq(name, "netband") && mt->rd != NULL) {
293                     mt->curband = NULL;
294                     goto done;
295           }
296 
297           /* <band> ... </band> */
298           if (iseq(name, "freqband") && mt->netband != NULL) {
299                     /* XXX handle inline freqbands */
300                     goto done;
301           }
302           if (iseq(name, "maxpower") && mt->netband != NULL) {
303                     mt->netband->maxPower = strtoul(p, NULL, 0);
304                     goto done;
305           }
306           if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
307                     mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
308                     goto done;
309           }
310           if (iseq(name, "maxantgain") && mt->netband != NULL) {
311                     mt->netband->maxAntGain = strtoul(p, NULL, 0);
312                     goto done;
313           }
314 
315           /* <country>...</country> */
316           if (iseq(name, "isocc") && mt->country != NULL) {
317                     mt->country->code = strtoul(p, NULL, 0);
318                     goto done;
319           }
320           if (iseq(name, "name") && mt->country != NULL) {
321                     mt->country->name = strdup(p);
322                     goto done;
323           }
324 
325           if (len != 0) {
326                     warnx("unexpected XML token \"%s\" data \"%s\" at line %ld",
327                         name, p, XML_GetCurrentLineNumber(mt->parser));
328                     /* XXX goto done? */
329           }
330           /* </freqband> */
331           if (iseq(name, "freqband") && mt->freqband != NULL) {
332                     /* XXX must have start/end frequencies */
333                     /* XXX must have channel width/sep */
334                     mt->freqband = NULL;
335                     goto done;
336           }
337           /* </rd> */
338           if (iseq(name, "rd") && mt->rd != NULL) {
339                     mt->rd = NULL;
340                     goto done;
341           }
342           /* </band> */
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));
347                     }
348                     if (mt->netband->maxPower == 0) {
349                               warnx("no maxpower for band at line %ld",
350                                  XML_GetCurrentLineNumber(mt->parser));
351                     }
352                     /* default max power w/ DFS to max power */
353                     if (mt->netband->maxPowerDFS == 0)
354                               mt->netband->maxPowerDFS = mt->netband->maxPower;
355                     mt->netband = NULL;
356                     goto done;
357           }
358           /* </netband> */
359           if (iseq(name, "netband") && mt->netband != NULL) {
360                     mt->curband = NULL;
361                     goto done;
362           }
363           /* </country> */
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));
368                     }
369                     if (mt->country->name == NULL) {
370                               warnx("no name for country at line %ld",
371                                  XML_GetCurrentLineNumber(mt->parser));
372                     }
373                     if (mt->country->rd == NULL) {
374                               warnx("no regdomain reference for country at line %ld",
375                                  XML_GetCurrentLineNumber(mt->parser));
376                     }
377                     mt->country = NULL;
378                     goto done;
379           }
380 done:
381           sbuf_delete(mt->sbuf[mt->level]);
382           mt->sbuf[mt->level--] = NULL;
383 }
384 
385 static void
char_data(void * data,const XML_Char * s,int len)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 *
findid(struct regdata * rdp,const void * id,enum IdentType type)404 findid(struct regdata *rdp, const void *id, enum IdentType type)
405 {
406           struct ident *ip;
407 
408           for (ip = rdp->ident; ip->id != NULL; ip++)
409                     if (ip->type == type && iseq(ip->id, id))
410                               return ip->p;
411           return NULL;
412 }
413 
414 /*
415  * Parse an regdomain XML configuration and build the internal representation.
416  */
417 int
lib80211_regdomain_readconfig(struct regdata * rdp,const void * p,size_t len)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
cleanup_bands(netband_head * head)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
lib80211_regdomain_cleanup(struct regdata * rdp)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 *
lib80211_alloc_regdata(void)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
lib80211_free_regdata(struct regdata * rdp)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 *
lib80211_regdomain_findbysku(const struct regdata * rdp,enum RegdomainCode sku)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 *
lib80211_regdomain_findbyname(const struct regdata * rdp,const char * name)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 (iseq(dp->name, name))
701                               return dp;
702           }
703           return NULL;
704 }
705 
706 /*
707  * Lookup a country by ISO country code.
708  */
709 const struct country *
lib80211_country_findbycc(const struct regdata * rdp,enum ISOCountryCode cc)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 *
lib80211_country_findbyname(const struct regdata * rdp,const char * name)725 lib80211_country_findbyname(const struct regdata *rdp, const char *name)
726 {
727           const struct country *cp;
728           int len;
729 
730           LIST_FOREACH(cp, &rdp->countries, next) {
731                     if (iseq(cp->isoname, name))
732                               return cp;
733           }
734           len = strlen(name);
735           LIST_FOREACH(cp, &rdp->countries, next) {
736                     if (strncasecmp(cp->name, name, len) == 0)
737                               return cp;
738           }
739           return NULL;
740 }
741