ifconfig(8): Use correct interface name when setting flags
[dragonfly.git] / sbin / ifconfig / 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  * $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
64 iseq(const char *a, const char *b)
65 {
66         return (strcasecmp(a, b) == 0);
67 }
68
69 static void
70 start_element(void *data, const char *name, const char **attr)
71 {
72         struct mystate *mt;
73         const void *id, *ref, *mode;
74         int i;
75
76         mt = data;
77         if (++mt->level == MAXLEVEL) {
78                 /* XXX force parser to abort */
79                 return;
80         }
81         mt->sbuf[mt->level] = sbuf_new_auto();
82         id = ref = mode = NULL;
83         for (i = 0; attr[i] != NULL; i += 2) {
84                 if (iseq(attr[i], "id")) {
85                         id = attr[i+1];
86                 } else if (iseq(attr[i], "ref")) {
87                         ref = attr[i+1];
88                 } else if (iseq(attr[i], "mode")) {
89                         mode = attr[i+1];
90                 } else
91                         printf("%*.*s[%s = %s]\n", mt->level + 1,
92                             mt->level + 1, "", attr[i], attr[i+1]);
93         }
94         if (iseq(name, "rd") && mt->rd == NULL) {
95                 if (mt->country == NULL) {
96                         mt->rd = calloc(1, sizeof(struct regdomain));
97                         mt->rd->name = strdup(id);
98                         mt->nident++;
99                         LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
100                 } else
101                         mt->country->rd = (void *)strdup(ref);
102                 return;
103         }
104         if (iseq(name, "defcc") && mt->rd != NULL) {
105                 mt->rd->cc = (void *)strdup(ref);
106                 return;
107         }
108         if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
109                 if (mode == NULL) {
110                         warnx("no mode for netband at line %ld",
111                             XML_GetCurrentLineNumber(mt->parser));
112                         return;
113                 }
114                 if (iseq(mode, "11b"))
115                         mt->curband = &mt->rd->bands_11b;
116                 else if (iseq(mode, "11g"))
117                         mt->curband = &mt->rd->bands_11g;
118                 else if (iseq(mode, "11a"))
119                         mt->curband = &mt->rd->bands_11a;
120                 else if (iseq(mode, "11ng"))
121                         mt->curband = &mt->rd->bands_11ng;
122                 else if (iseq(mode, "11na"))
123                         mt->curband = &mt->rd->bands_11na;
124                 else
125                         warnx("unknown mode \"%s\" at line %ld",
126                             __DECONST(char *, mode),
127                             XML_GetCurrentLineNumber(mt->parser));
128                 return;
129         }
130         if (iseq(name, "band") && mt->netband == NULL) {
131                 if (mt->curband == NULL) {
132                         warnx("band without enclosing netband at line %ld",
133                             XML_GetCurrentLineNumber(mt->parser));
134                         return;
135                 }
136                 mt->netband = calloc(1, sizeof(struct netband));
137                 LIST_INSERT_HEAD(mt->curband, mt->netband, next);
138                 return;
139         }
140         if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) {
141                 /* XXX handle inlines and merge into table? */
142                 if (mt->netband->band != NULL) {
143                         warnx("duplicate freqband at line %ld ignored",
144                             XML_GetCurrentLineNumber(mt->parser));
145                         /* XXX complain */
146                 } else
147                         mt->netband->band = (void *)strdup(ref);
148                 return;
149         }
150
151         if (iseq(name, "country") && mt->country == NULL) {
152                 mt->country = calloc(1, sizeof(struct country));
153                 mt->country->isoname = strdup(id);
154                 mt->country->code = NO_COUNTRY;
155                 mt->nident++;
156                 LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
157                 return;
158         }
159
160         if (iseq(name, "freqband") && mt->freqband == NULL) {
161                 mt->freqband = calloc(1, sizeof(struct freqband));
162                 mt->freqband->id = strdup(id);
163                 mt->nident++;
164                 LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
165                 return;
166         }
167 }
168
169 static int
170 decode_flag(struct mystate *mt, const char *p, int len)
171 {
172         static const struct {
173                 const char *name;
174                 int len;
175                 uint32_t value;
176         } flags[] = {
177 #define FLAG(x) { #x, sizeof(#x)-1, x }
178                 FLAG(IEEE80211_CHAN_A),
179                 FLAG(IEEE80211_CHAN_B),
180                 FLAG(IEEE80211_CHAN_G),
181                 FLAG(IEEE80211_CHAN_HT20),
182                 FLAG(IEEE80211_CHAN_HT40),
183                 FLAG(IEEE80211_CHAN_ST),
184                 FLAG(IEEE80211_CHAN_TURBO),
185                 FLAG(IEEE80211_CHAN_PASSIVE),
186                 FLAG(IEEE80211_CHAN_DFS),
187                 FLAG(IEEE80211_CHAN_CCK),
188                 FLAG(IEEE80211_CHAN_OFDM),
189                 FLAG(IEEE80211_CHAN_2GHZ),
190                 FLAG(IEEE80211_CHAN_5GHZ),
191                 FLAG(IEEE80211_CHAN_DYN),
192                 FLAG(IEEE80211_CHAN_GFSK),
193                 FLAG(IEEE80211_CHAN_GSM),
194                 FLAG(IEEE80211_CHAN_STURBO),
195                 FLAG(IEEE80211_CHAN_HALF),
196                 FLAG(IEEE80211_CHAN_QUARTER),
197                 FLAG(IEEE80211_CHAN_HT40U),
198                 FLAG(IEEE80211_CHAN_HT40D),
199                 FLAG(IEEE80211_CHAN_4MSXMIT),
200                 FLAG(IEEE80211_CHAN_NOADHOC),
201                 FLAG(IEEE80211_CHAN_NOHOSTAP),
202                 FLAG(IEEE80211_CHAN_11D),
203                 FLAG(IEEE80211_CHAN_FHSS),
204                 FLAG(IEEE80211_CHAN_PUREG),
205                 FLAG(IEEE80211_CHAN_108A),
206                 FLAG(IEEE80211_CHAN_108G),
207 #undef FLAG
208                 { "ECM",        3,      REQ_ECM },
209                 { "INDOOR",     6,      REQ_INDOOR },
210                 { "OUTDOOR",    7,      REQ_OUTDOOR },
211         };
212         size_t i;
213
214         for (i = 0; i < nitems(flags); i++)
215                 if (len == flags[i].len && iseq(p, flags[i].name))
216                         return flags[i].value;
217         warnx("unknown flag \"%.*s\" at line %ld ignored",
218             len, p, XML_GetCurrentLineNumber(mt->parser));
219         return 0;
220 }
221
222 static void
223 end_element(void *data, const char *name)
224 {
225         struct mystate *mt;
226         int len;
227         char *p;
228
229         mt = data;
230         sbuf_finish(mt->sbuf[mt->level]);
231         p = sbuf_data(mt->sbuf[mt->level]);
232         len = sbuf_len(mt->sbuf[mt->level]);
233
234         /* <freqband>...</freqband> */
235         if (iseq(name, "freqstart") && mt->freqband != NULL) {
236                 mt->freqband->freqStart = strtoul(p, NULL, 0);
237                 goto done;
238         }
239         if (iseq(name, "freqend") && mt->freqband != NULL) {
240                 mt->freqband->freqEnd = strtoul(p, NULL, 0);
241                 goto done;
242         }
243         if (iseq(name, "chanwidth") && mt->freqband != NULL) {
244                 mt->freqband->chanWidth = strtoul(p, NULL, 0);
245                 goto done;
246         }
247         if (iseq(name, "chansep") && mt->freqband != NULL) {
248                 mt->freqband->chanSep = strtoul(p, NULL, 0);
249                 goto done;
250         }
251         if (iseq(name, "flags")) {
252                 if (mt->freqband != NULL)
253                         mt->freqband->flags |= decode_flag(mt, p, len);
254                 else if (mt->netband != NULL)
255                         mt->netband->flags |= decode_flag(mt, p, len);
256                 else {
257                         warnx("flags without freqband or netband at line %ld ignored",
258                             XML_GetCurrentLineNumber(mt->parser));
259                 }
260                 goto done;
261         }
262
263         /* <rd> ... </rd> */
264         if (iseq(name, "name") && mt->rd != NULL) {
265                 mt->rd->name = strdup(p);
266                 goto done;
267         }
268         if (iseq(name, "sku") && mt->rd != NULL) {
269                 mt->rd->sku = strtoul(p, NULL, 0);
270                 goto done;
271         }
272         if (iseq(name, "netband") && mt->rd != NULL) {
273                 mt->curband = NULL;
274                 goto done;
275         }
276
277         /* <band> ... </band> */
278         if (iseq(name, "freqband") && mt->netband != NULL) {
279                 /* XXX handle inline freqbands */
280                 goto done;
281         }
282         if (iseq(name, "maxpower") && mt->netband != NULL) {
283                 mt->netband->maxPower = strtoul(p, NULL, 0);
284                 goto done;
285         }
286         if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
287                 mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
288                 goto done;
289         }
290         if (iseq(name, "maxantgain") && mt->netband != NULL) {
291                 mt->netband->maxAntGain = strtoul(p, NULL, 0);
292                 goto done;
293         }
294
295         /* <country>...</country> */
296         if (iseq(name, "isocc") && mt->country != NULL) {
297                 mt->country->code = strtoul(p, NULL, 0);
298                 goto done;
299         }
300         if (iseq(name, "name") && mt->country != NULL) {
301                 mt->country->name = strdup(p);
302                 goto done;
303         }
304
305         if (len != 0) {
306                 warnx("unexpected XML token \"%s\" data \"%s\" at line %ld",
307                     name, p, XML_GetCurrentLineNumber(mt->parser));
308                 /* XXX goto done? */
309         }
310         /* </freqband> */
311         if (iseq(name, "freqband") && mt->freqband != NULL) {
312                 /* XXX must have start/end frequencies */
313                 /* XXX must have channel width/sep */
314                 mt->freqband = NULL;
315                 goto done;
316         }
317         /* </rd> */
318         if (iseq(name, "rd") && mt->rd != NULL) {
319                 mt->rd = NULL;
320                 goto done;
321         }
322         /* </band> */
323         if (iseq(name, "band") && mt->netband != NULL) {
324                 if (mt->netband->band == NULL) {
325                         warnx("no freqbands for band at line %ld",
326                            XML_GetCurrentLineNumber(mt->parser));
327                 }
328                 if (mt->netband->maxPower == 0) {
329                         warnx("no maxpower for band at line %ld",
330                            XML_GetCurrentLineNumber(mt->parser));
331                 }
332                 /* default max power w/ DFS to max power */
333                 if (mt->netband->maxPowerDFS == 0)
334                         mt->netband->maxPowerDFS = mt->netband->maxPower;
335                 mt->netband = NULL;
336                 goto done;
337         }
338         /* </netband> */
339         if (iseq(name, "netband") && mt->netband != NULL) {
340                 mt->curband = NULL;
341                 goto done;
342         }
343         /* </country> */
344         if (iseq(name, "country") && mt->country != NULL) {
345                 if (mt->country->code == NO_COUNTRY) {
346                         warnx("no ISO cc for country at line %ld",
347                            XML_GetCurrentLineNumber(mt->parser));
348                 }
349                 if (mt->country->name == NULL) {
350                         warnx("no name for country at line %ld",
351                            XML_GetCurrentLineNumber(mt->parser));
352                 }
353                 if (mt->country->rd == NULL) {
354                         warnx("no regdomain reference for country at line %ld",
355                            XML_GetCurrentLineNumber(mt->parser));
356                 }
357                 mt->country = NULL;
358                 goto done;
359         }
360 done:
361         sbuf_delete(mt->sbuf[mt->level]);
362         mt->sbuf[mt->level--] = NULL;
363 }
364
365 static void
366 char_data(void *data, const XML_Char *s, int len)
367 {
368         struct mystate *mt;
369         const char *b, *e;
370
371         mt = data;
372
373         b = s;
374         e = s + len-1;
375         for (; isspace(*b) && b < e; b++)
376                 ;
377         for (; isspace(*e) && e > b; e++)
378                 ;
379         if (e != b || (*b != '\0' && !isspace(*b)))
380                 sbuf_bcat(mt->sbuf[mt->level], b, e-b+1);
381 }
382
383 static void *
384 findid(struct regdata *rdp, const void *id, enum IdentType type)
385 {
386         struct ident *ip;
387
388         for (ip = rdp->ident; ip->id != NULL; ip++)
389                 if (ip->type == type && iseq(ip->id, id))
390                         return ip->p;
391         return NULL;
392 }
393
394 /*
395  * Parse an regdomain XML configuration and build the internal representation.
396  */
397 int
398 lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len)
399 {
400         struct mystate *mt;
401         struct regdomain *dp;
402         struct country *cp;
403         struct freqband *fp;
404         struct netband *nb;
405         const void *id;
406         int i, errors;
407
408         memset(rdp, 0, sizeof(struct regdata));
409         mt = calloc(1, sizeof(struct mystate));
410         if (mt == NULL)
411                 return ENOMEM;
412         /* parse the XML input */
413         mt->rdp = rdp;
414         mt->parser = XML_ParserCreate(NULL);
415         XML_SetUserData(mt->parser, mt);
416         XML_SetElementHandler(mt->parser, start_element, end_element);
417         XML_SetCharacterDataHandler(mt->parser, char_data);
418         if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) {
419                 warnx("%s: %s at line %ld", __func__,
420                    XML_ErrorString(XML_GetErrorCode(mt->parser)),
421                    XML_GetCurrentLineNumber(mt->parser));
422                 return -1;
423         }
424         XML_ParserFree(mt->parser);
425
426         /* setup the identifer table */
427         rdp->ident = calloc(sizeof(struct ident), mt->nident + 1);
428         if (rdp->ident == NULL)
429                 return ENOMEM;
430         free(mt);
431
432         errors = 0;
433         i = 0;
434         LIST_FOREACH(dp, &rdp->domains, next) {
435                 rdp->ident[i].id = dp->name;
436                 rdp->ident[i].p = dp;
437                 rdp->ident[i].type = DOMAIN;
438                 i++;
439         }
440         LIST_FOREACH(fp, &rdp->freqbands, next) {
441                 rdp->ident[i].id = fp->id;
442                 rdp->ident[i].p = fp;
443                 rdp->ident[i].type = FREQBAND;
444                 i++;
445         }
446         LIST_FOREACH(cp, &rdp->countries, next) {
447                 rdp->ident[i].id = cp->isoname;
448                 rdp->ident[i].p = cp;
449                 rdp->ident[i].type = COUNTRY;
450                 i++;
451         }
452
453         /* patch references */
454         LIST_FOREACH(dp, &rdp->domains, next) {
455                 if (dp->cc != NULL) {
456                         id = dp->cc;
457                         dp->cc = findid(rdp, id, COUNTRY);
458                         if (dp->cc == NULL) {
459                                 warnx("undefined country \"%s\"",
460                                     __DECONST(char *, id));
461                                 errors++;
462                         }
463                         free(__DECONST(char *, id));
464                 }
465                 LIST_FOREACH(nb, &dp->bands_11b, next) {
466                         id = findid(rdp, nb->band, FREQBAND);
467                         if (id == NULL) {
468                                 warnx("undefined 11b band \"%s\"",
469                                     __DECONST(char *, nb->band));
470                                 errors++;
471                         }
472                         nb->band = id;
473                 }
474                 LIST_FOREACH(nb, &dp->bands_11g, next) {
475                         id = findid(rdp, nb->band, FREQBAND);
476                         if (id == NULL) {
477                                 warnx("undefined 11g band \"%s\"",
478                                     __DECONST(char *, nb->band));
479                                 errors++;
480                         }
481                         nb->band = id;
482                 }
483                 LIST_FOREACH(nb, &dp->bands_11a, next) {
484                         id = findid(rdp, nb->band, FREQBAND);
485                         if (id == NULL) {
486                                 warnx("undefined 11a band \"%s\"",
487                                     __DECONST(char *, nb->band));
488                                 errors++;
489                         }
490                         nb->band = id;
491                 }
492                 LIST_FOREACH(nb, &dp->bands_11ng, next) {
493                         id = findid(rdp, nb->band, FREQBAND);
494                         if (id == NULL) {
495                                 warnx("undefined 11ng band \"%s\"",
496                                     __DECONST(char *, nb->band));
497                                 errors++;
498                         }
499                         nb->band = id;
500                 }
501                 LIST_FOREACH(nb, &dp->bands_11na, next) {
502                         id = findid(rdp, nb->band, FREQBAND);
503                         if (id == NULL) {
504                                 warnx("undefined 11na band \"%s\"",
505                                     __DECONST(char *, nb->band));
506                                 errors++;
507                         }
508                         nb->band = id;
509                 }
510         }
511         LIST_FOREACH(cp, &rdp->countries, next) {
512                 id = cp->rd;
513                 cp->rd = findid(rdp, id, DOMAIN);
514                 if (cp->rd == NULL) {
515                         warnx("undefined country \"%s\"",
516                             __DECONST(char *, id));
517                         errors++;
518                 }
519                 free(__DECONST(char *, id));
520         }
521
522         return errors ? EINVAL : 0;
523 }
524
525 static void
526 cleanup_bands(netband_head *head)
527 {
528         struct netband *nb;
529
530         for (;;) {
531                 nb = LIST_FIRST(head);
532                 if (nb == NULL)
533                         break;
534                 free(nb);
535         }
536 }
537
538 /*
539  * Cleanup state/resources for a previously parsed regdomain database.
540  */
541 void
542 lib80211_regdomain_cleanup(struct regdata *rdp)
543 {
544
545         free(rdp->ident);
546         rdp->ident = NULL;
547         for (;;) {
548                 struct regdomain *dp = LIST_FIRST(&rdp->domains);
549                 if (dp == NULL)
550                         break;
551                 LIST_REMOVE(dp, next);
552                 cleanup_bands(&dp->bands_11b);
553                 cleanup_bands(&dp->bands_11g);
554                 cleanup_bands(&dp->bands_11a);
555                 cleanup_bands(&dp->bands_11ng);
556                 cleanup_bands(&dp->bands_11na);
557                 if (dp->name != NULL)
558                         free(__DECONST(char *, dp->name));
559         }
560         for (;;) {
561                 struct country *cp = LIST_FIRST(&rdp->countries);
562                 if (cp == NULL)
563                         break;
564                 LIST_REMOVE(cp, next);
565                 if (cp->name != NULL)
566                         free(__DECONST(char *, cp->name));
567                 free(cp);
568         }
569         for (;;) {
570                 struct freqband *fp = LIST_FIRST(&rdp->freqbands);
571                 if (fp == NULL)
572                         break;
573                 LIST_REMOVE(fp, next);
574                 free(fp);
575         }
576 }
577
578 struct regdata *
579 lib80211_alloc_regdata(void)
580 {
581         struct regdata *rdp;
582         struct stat sb;
583         void *xml;
584         int fd;
585
586         rdp = calloc(1, sizeof(struct regdata));
587
588         fd = open(_PATH_REGDOMAIN, O_RDONLY);
589         if (fd < 0) {
590 #ifdef DEBUG
591                 warn("%s: open(%s)", __func__, _PATH_REGDOMAIN);
592 #endif
593                 free(rdp);
594                 return NULL;
595         }
596         if (fstat(fd, &sb) < 0) {
597 #ifdef DEBUG
598                 warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN);
599 #endif
600                 close(fd);
601                 free(rdp);
602                 return NULL;
603         }
604         xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
605         if (xml == MAP_FAILED) {
606 #ifdef DEBUG
607                 warn("%s: mmap", __func__);
608 #endif
609                 close(fd);
610                 free(rdp);
611                 return NULL;
612         }
613         if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) {
614 #ifdef DEBUG
615                 warn("%s: error reading regulatory database", __func__);
616 #endif
617                 munmap(xml, sb.st_size);
618                 close(fd);
619                 free(rdp);
620                 return NULL;
621         }
622         munmap(xml, sb.st_size);
623         close(fd);
624
625         return rdp;
626 }
627
628 void
629 lib80211_free_regdata(struct regdata *rdp)
630 {
631         lib80211_regdomain_cleanup(rdp);
632         free(rdp);
633 }
634
635 /*
636  * Lookup a regdomain by SKU.
637  */
638 const struct regdomain *
639 lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku)
640 {
641         const struct regdomain *dp;
642
643         LIST_FOREACH(dp, &rdp->domains, next) {
644                 if (dp->sku == sku)
645                         return dp;
646         }
647         return NULL;
648 }
649
650 /*
651  * Lookup a regdomain by name.
652  */
653 const struct regdomain *
654 lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name)
655 {
656         const struct regdomain *dp;
657
658         LIST_FOREACH(dp, &rdp->domains, next) {
659                 if (iseq(dp->name, name))
660                         return dp;
661         }
662         return NULL;
663 }
664
665 /*
666  * Lookup a country by ISO country code.
667  */
668 const struct country *
669 lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc)
670 {
671         const struct country *cp;
672
673         LIST_FOREACH(cp, &rdp->countries, next) {
674                 if (cp->code == cc)
675                         return cp;
676         }
677         return NULL;
678 }
679
680 /*
681  * Lookup a country by ISO/long name.
682  */
683 const struct country *
684 lib80211_country_findbyname(const struct regdata *rdp, const char *name)
685 {
686         const struct country *cp;
687         int len;
688
689         LIST_FOREACH(cp, &rdp->countries, next) {
690                 if (iseq(cp->isoname, name))
691                         return cp;
692         }
693         len = strlen(name);
694         LIST_FOREACH(cp, &rdp->countries, next) {
695                 if (strncasecmp(cp->name, name, len) == 0)
696                         return cp;
697         }
698         return NULL;
699 }