2 * Copyright (c) 2008 Sam Leffler, Errno Consulting
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 static const char rcsid
[] = "$FreeBSD: head/sbin/ifconfig/regdomain.c 200587 2009-12-15 20:44:12Z gavin $";
29 #include <sys/types.h>
30 #include <sys/errno.h>
44 #include "regdomain.h"
46 #include <netproto/802_11/_ieee80211.h>
53 struct regdomain
*rd
; /* current domain */
54 struct netband
*netband
; /* current netband */
55 struct freqband
*freqband
; /* current freqband */
56 struct country
*country
; /* current country */
57 netband_head
*curband
; /* current netband list */
59 struct sbuf
*sbuf
[MAXLEVEL
];
66 enum { DOMAIN
, COUNTRY
, FREQBAND
} type
;
70 start_element(void *data
, const char *name
, const char **attr
)
72 #define iseq(a,b) (strcasecmp(a,b) == 0)
74 const void *id
, *ref
, *mode
;
78 if (++mt
->level
== MAXLEVEL
) {
79 /* XXX force parser to abort */
82 mt
->sbuf
[mt
->level
] = sbuf_new_auto();
83 id
= ref
= mode
= NULL
;
84 for (i
= 0; attr
[i
] != NULL
; i
+= 2) {
85 if (iseq(attr
[i
], "id")) {
87 } else if (iseq(attr
[i
], "ref")) {
89 } else if (iseq(attr
[i
], "mode")) {
92 printf("%*.*s[%s = %s]\n", mt
->level
+ 1,
93 mt
->level
+ 1, "", attr
[i
], attr
[i
+1]);
95 if (iseq(name
, "rd") && mt
->rd
== NULL
) {
96 if (mt
->country
== NULL
) {
97 mt
->rd
= calloc(1, sizeof(struct regdomain
));
98 mt
->rd
->name
= strdup(id
);
100 LIST_INSERT_HEAD(&mt
->rdp
->domains
, mt
->rd
, next
);
102 mt
->country
->rd
= (void *)strdup(ref
);
105 if (iseq(name
, "defcc") && mt
->rd
!= NULL
) {
106 mt
->rd
->cc
= (void *)strdup(ref
);
109 if (iseq(name
, "netband") && mt
->curband
== NULL
&& mt
->rd
!= NULL
) {
111 warnx("no mode for netband at line %ld",
112 XML_GetCurrentLineNumber(mt
->parser
));
115 if (iseq(mode
, "11b"))
116 mt
->curband
= &mt
->rd
->bands_11b
;
117 else if (iseq(mode
, "11g"))
118 mt
->curband
= &mt
->rd
->bands_11g
;
119 else if (iseq(mode
, "11a"))
120 mt
->curband
= &mt
->rd
->bands_11a
;
121 else if (iseq(mode
, "11ng"))
122 mt
->curband
= &mt
->rd
->bands_11ng
;
123 else if (iseq(mode
, "11na"))
124 mt
->curband
= &mt
->rd
->bands_11na
;
126 warnx("unknown mode \"%s\" at line %ld",
127 __DECONST(char *, mode
),
128 XML_GetCurrentLineNumber(mt
->parser
));
131 if (iseq(name
, "band") && mt
->netband
== NULL
) {
132 if (mt
->curband
== NULL
) {
133 warnx("band without enclosing netband at line %ld",
134 XML_GetCurrentLineNumber(mt
->parser
));
137 mt
->netband
= calloc(1, sizeof(struct netband
));
138 LIST_INSERT_HEAD(mt
->curband
, mt
->netband
, next
);
141 if (iseq(name
, "freqband") && mt
->freqband
== NULL
&& mt
->netband
!= NULL
) {
142 /* XXX handle inlines and merge into table? */
143 if (mt
->netband
->band
!= NULL
) {
144 warnx("duplicate freqband at line %ld ignored",
145 XML_GetCurrentLineNumber(mt
->parser
));
148 mt
->netband
->band
= (void *)strdup(ref
);
152 if (iseq(name
, "country") && mt
->country
== NULL
) {
153 mt
->country
= calloc(1, sizeof(struct country
));
154 mt
->country
->isoname
= strdup(id
);
155 mt
->country
->code
= NO_COUNTRY
;
157 LIST_INSERT_HEAD(&mt
->rdp
->countries
, mt
->country
, next
);
161 if (iseq(name
, "freqband") && mt
->freqband
== NULL
) {
162 mt
->freqband
= calloc(1, sizeof(struct freqband
));
163 mt
->freqband
->id
= strdup(id
);
165 LIST_INSERT_HEAD(&mt
->rdp
->freqbands
, mt
->freqband
, next
);
172 decode_flag(struct mystate
*mt
, const char *p
, int len
)
174 #define iseq(a,b) (strcasecmp(a,b) == 0)
175 static const struct {
180 #define FLAG(x) { #x, sizeof(#x)-1, x }
181 FLAG(IEEE80211_CHAN_A
),
182 FLAG(IEEE80211_CHAN_B
),
183 FLAG(IEEE80211_CHAN_G
),
184 FLAG(IEEE80211_CHAN_HT20
),
185 FLAG(IEEE80211_CHAN_HT40
),
186 FLAG(IEEE80211_CHAN_ST
),
187 FLAG(IEEE80211_CHAN_TURBO
),
188 FLAG(IEEE80211_CHAN_PASSIVE
),
189 FLAG(IEEE80211_CHAN_DFS
),
190 FLAG(IEEE80211_CHAN_CCK
),
191 FLAG(IEEE80211_CHAN_OFDM
),
192 FLAG(IEEE80211_CHAN_2GHZ
),
193 FLAG(IEEE80211_CHAN_5GHZ
),
194 FLAG(IEEE80211_CHAN_DYN
),
195 FLAG(IEEE80211_CHAN_GFSK
),
196 FLAG(IEEE80211_CHAN_GSM
),
197 FLAG(IEEE80211_CHAN_STURBO
),
198 FLAG(IEEE80211_CHAN_HALF
),
199 FLAG(IEEE80211_CHAN_QUARTER
),
200 FLAG(IEEE80211_CHAN_HT40U
),
201 FLAG(IEEE80211_CHAN_HT40D
),
202 FLAG(IEEE80211_CHAN_4MSXMIT
),
203 FLAG(IEEE80211_CHAN_NOADHOC
),
204 FLAG(IEEE80211_CHAN_NOHOSTAP
),
205 FLAG(IEEE80211_CHAN_11D
),
206 FLAG(IEEE80211_CHAN_FHSS
),
207 FLAG(IEEE80211_CHAN_PUREG
),
208 FLAG(IEEE80211_CHAN_108A
),
209 FLAG(IEEE80211_CHAN_108G
),
211 { "ECM", 3, REQ_ECM
},
212 { "INDOOR", 6, REQ_INDOOR
},
213 { "OUTDOOR", 7, REQ_OUTDOOR
},
217 for (i
= 0; i
< sizeof(flags
)/sizeof(flags
[0]); i
++)
218 if (len
== flags
[i
].len
&& iseq(p
, flags
[i
].name
))
219 return flags
[i
].value
;
220 warnx("unknown flag \"%.*s\" at line %ld ignored",
221 len
, p
, XML_GetCurrentLineNumber(mt
->parser
));
227 end_element(void *data
, const char *name
)
229 #define iseq(a,b) (strcasecmp(a,b) == 0)
235 sbuf_finish(mt
->sbuf
[mt
->level
]);
236 p
= sbuf_data(mt
->sbuf
[mt
->level
]);
237 len
= sbuf_len(mt
->sbuf
[mt
->level
]);
239 /* <freqband>...</freqband> */
240 if (iseq(name
, "freqstart") && mt
->freqband
!= NULL
) {
241 mt
->freqband
->freqStart
= strtoul(p
, NULL
, 0);
244 if (iseq(name
, "freqend") && mt
->freqband
!= NULL
) {
245 mt
->freqband
->freqEnd
= strtoul(p
, NULL
, 0);
248 if (iseq(name
, "chanwidth") && mt
->freqband
!= NULL
) {
249 mt
->freqband
->chanWidth
= strtoul(p
, NULL
, 0);
252 if (iseq(name
, "chansep") && mt
->freqband
!= NULL
) {
253 mt
->freqband
->chanSep
= strtoul(p
, NULL
, 0);
256 if (iseq(name
, "flags")) {
257 if (mt
->freqband
!= NULL
)
258 mt
->freqband
->flags
|= decode_flag(mt
, p
, len
);
259 else if (mt
->netband
!= NULL
)
260 mt
->netband
->flags
|= decode_flag(mt
, p
, len
);
262 warnx("flags without freqband or netband at line %ld ignored",
263 XML_GetCurrentLineNumber(mt
->parser
));
269 if (iseq(name
, "name") && mt
->rd
!= NULL
) {
270 mt
->rd
->name
= strdup(p
);
273 if (iseq(name
, "sku") && mt
->rd
!= NULL
) {
274 mt
->rd
->sku
= strtoul(p
, NULL
, 0);
277 if (iseq(name
, "netband") && mt
->rd
!= NULL
) {
282 /* <band> ... </band> */
283 if (iseq(name
, "freqband") && mt
->netband
!= NULL
) {
284 /* XXX handle inline freqbands */
287 if (iseq(name
, "maxpower") && mt
->netband
!= NULL
) {
288 mt
->netband
->maxPower
= strtoul(p
, NULL
, 0);
291 if (iseq(name
, "maxpowerdfs") && mt
->netband
!= NULL
) {
292 mt
->netband
->maxPowerDFS
= strtoul(p
, NULL
, 0);
295 if (iseq(name
, "maxantgain") && mt
->netband
!= NULL
) {
296 mt
->netband
->maxAntGain
= strtoul(p
, NULL
, 0);
300 /* <country>...</country> */
301 if (iseq(name
, "isocc") && mt
->country
!= NULL
) {
302 mt
->country
->code
= strtoul(p
, NULL
, 0);
305 if (iseq(name
, "name") && mt
->country
!= NULL
) {
306 mt
->country
->name
= strdup(p
);
311 warnx("unexpected XML token \"%s\" data \"%s\" at line %ld",
312 name
, p
, XML_GetCurrentLineNumber(mt
->parser
));
316 if (iseq(name
, "freqband") && mt
->freqband
!= NULL
) {
317 /* XXX must have start/end frequencies */
318 /* XXX must have channel width/sep */
323 if (iseq(name
, "rd") && mt
->rd
!= NULL
) {
328 if (iseq(name
, "band") && mt
->netband
!= NULL
) {
329 if (mt
->netband
->band
== NULL
) {
330 warnx("no freqbands for band at line %ld",
331 XML_GetCurrentLineNumber(mt
->parser
));
333 if (mt
->netband
->maxPower
== 0) {
334 warnx("no maxpower for band at line %ld",
335 XML_GetCurrentLineNumber(mt
->parser
));
337 /* default max power w/ DFS to max power */
338 if (mt
->netband
->maxPowerDFS
== 0)
339 mt
->netband
->maxPowerDFS
= mt
->netband
->maxPower
;
344 if (iseq(name
, "netband") && mt
->netband
!= NULL
) {
349 if (iseq(name
, "country") && mt
->country
!= NULL
) {
350 if (mt
->country
->code
== NO_COUNTRY
) {
351 warnx("no ISO cc for country at line %ld",
352 XML_GetCurrentLineNumber(mt
->parser
));
354 if (mt
->country
->name
== NULL
) {
355 warnx("no name for country at line %ld",
356 XML_GetCurrentLineNumber(mt
->parser
));
358 if (mt
->country
->rd
== NULL
) {
359 warnx("no regdomain reference for country at line %ld",
360 XML_GetCurrentLineNumber(mt
->parser
));
366 sbuf_delete(mt
->sbuf
[mt
->level
]);
367 mt
->sbuf
[mt
->level
--] = NULL
;
372 char_data(void *data
, const XML_Char
*s
, int len
)
381 for (; isspace(*b
) && b
< e
; b
++)
383 for (; isspace(*e
) && e
> b
; e
++)
385 if (e
!= b
|| (*b
!= '\0' && !isspace(*b
)))
386 sbuf_bcat(mt
->sbuf
[mt
->level
], b
, e
-b
+1);
390 findid(struct regdata
*rdp
, const void *id
, int type
)
394 for (ip
= rdp
->ident
; ip
->id
!= NULL
; ip
++)
395 if (ip
->type
== type
&& strcasecmp(ip
->id
, id
) == 0)
401 * Parse an regdomain XML configuration and build the internal representation.
404 lib80211_regdomain_readconfig(struct regdata
*rdp
, const void *p
, size_t len
)
407 struct regdomain
*dp
;
414 memset(rdp
, 0, sizeof(struct regdata
));
415 mt
= calloc(1, sizeof(struct mystate
));
418 /* parse the XML input */
420 mt
->parser
= XML_ParserCreate(NULL
);
421 XML_SetUserData(mt
->parser
, mt
);
422 XML_SetElementHandler(mt
->parser
, start_element
, end_element
);
423 XML_SetCharacterDataHandler(mt
->parser
, char_data
);
424 if (XML_Parse(mt
->parser
, p
, len
, 1) != XML_STATUS_OK
) {
425 warnx("%s: %s at line %ld", __func__
,
426 XML_ErrorString(XML_GetErrorCode(mt
->parser
)),
427 XML_GetCurrentLineNumber(mt
->parser
));
430 XML_ParserFree(mt
->parser
);
432 /* setup the identifer table */
433 rdp
->ident
= calloc(sizeof(struct ident
), mt
->nident
+ 1);
434 if (rdp
->ident
== NULL
)
440 LIST_FOREACH(dp
, &rdp
->domains
, next
) {
441 rdp
->ident
[i
].id
= dp
->name
;
442 rdp
->ident
[i
].p
= dp
;
443 rdp
->ident
[i
].type
= DOMAIN
;
446 LIST_FOREACH(fp
, &rdp
->freqbands
, next
) {
447 rdp
->ident
[i
].id
= fp
->id
;
448 rdp
->ident
[i
].p
= fp
;
449 rdp
->ident
[i
].type
= FREQBAND
;
452 LIST_FOREACH(cp
, &rdp
->countries
, next
) {
453 rdp
->ident
[i
].id
= cp
->isoname
;
454 rdp
->ident
[i
].p
= cp
;
455 rdp
->ident
[i
].type
= COUNTRY
;
459 /* patch references */
460 LIST_FOREACH(dp
, &rdp
->domains
, next
) {
461 if (dp
->cc
!= NULL
) {
463 dp
->cc
= findid(rdp
, id
, COUNTRY
);
464 if (dp
->cc
== NULL
) {
465 warnx("undefined country \"%s\"",
466 __DECONST(char *, id
));
469 free(__DECONST(char *, id
));
471 LIST_FOREACH(nb
, &dp
->bands_11b
, next
) {
472 id
= findid(rdp
, nb
->band
, FREQBAND
);
474 warnx("undefined 11b band \"%s\"",
475 __DECONST(char *, nb
->band
));
480 LIST_FOREACH(nb
, &dp
->bands_11g
, next
) {
481 id
= findid(rdp
, nb
->band
, FREQBAND
);
483 warnx("undefined 11g band \"%s\"",
484 __DECONST(char *, nb
->band
));
489 LIST_FOREACH(nb
, &dp
->bands_11a
, next
) {
490 id
= findid(rdp
, nb
->band
, FREQBAND
);
492 warnx("undefined 11a band \"%s\"",
493 __DECONST(char *, nb
->band
));
498 LIST_FOREACH(nb
, &dp
->bands_11ng
, next
) {
499 id
= findid(rdp
, nb
->band
, FREQBAND
);
501 warnx("undefined 11ng band \"%s\"",
502 __DECONST(char *, nb
->band
));
507 LIST_FOREACH(nb
, &dp
->bands_11na
, next
) {
508 id
= findid(rdp
, nb
->band
, FREQBAND
);
510 warnx("undefined 11na band \"%s\"",
511 __DECONST(char *, nb
->band
));
517 LIST_FOREACH(cp
, &rdp
->countries
, next
) {
519 cp
->rd
= findid(rdp
, id
, DOMAIN
);
520 if (cp
->rd
== NULL
) {
521 warnx("undefined country \"%s\"",
522 __DECONST(char *, id
));
525 free(__DECONST(char *, id
));
528 return errors
? EINVAL
: 0;
532 cleanup_bands(netband_head
*head
)
537 nb
= LIST_FIRST(head
);
545 * Cleanup state/resources for a previously parsed regdomain database.
548 lib80211_regdomain_cleanup(struct regdata
*rdp
)
554 struct regdomain
*dp
= LIST_FIRST(&rdp
->domains
);
557 LIST_REMOVE(dp
, next
);
558 cleanup_bands(&dp
->bands_11b
);
559 cleanup_bands(&dp
->bands_11g
);
560 cleanup_bands(&dp
->bands_11a
);
561 cleanup_bands(&dp
->bands_11ng
);
562 cleanup_bands(&dp
->bands_11na
);
563 if (dp
->name
!= NULL
)
564 free(__DECONST(char *, dp
->name
));
567 struct country
*cp
= LIST_FIRST(&rdp
->countries
);
570 LIST_REMOVE(cp
, next
);
571 if (cp
->name
!= NULL
)
572 free(__DECONST(char *, cp
->name
));
576 struct freqband
*fp
= LIST_FIRST(&rdp
->freqbands
);
579 LIST_REMOVE(fp
, next
);
585 lib80211_alloc_regdata(void)
592 rdp
= calloc(1, sizeof(struct regdata
));
594 fd
= open(_PATH_REGDOMAIN
, O_RDONLY
);
597 warn("%s: open(%s)", __func__
, _PATH_REGDOMAIN
);
602 if (fstat(fd
, &sb
) < 0) {
604 warn("%s: fstat(%s)", __func__
, _PATH_REGDOMAIN
);
610 xml
= mmap(NULL
, sb
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
611 if (xml
== MAP_FAILED
) {
613 warn("%s: mmap", __func__
);
619 if (lib80211_regdomain_readconfig(rdp
, xml
, sb
.st_size
) != 0) {
621 warn("%s: error reading regulatory database", __func__
);
623 munmap(xml
, sb
.st_size
);
628 munmap(xml
, sb
.st_size
);
635 lib80211_free_regdata(struct regdata
*rdp
)
637 lib80211_regdomain_cleanup(rdp
);
642 * Lookup a regdomain by SKU.
644 const struct regdomain
*
645 lib80211_regdomain_findbysku(const struct regdata
*rdp
, enum RegdomainCode sku
)
647 const struct regdomain
*dp
;
649 LIST_FOREACH(dp
, &rdp
->domains
, next
) {
657 * Lookup a regdomain by name.
659 const struct regdomain
*
660 lib80211_regdomain_findbyname(const struct regdata
*rdp
, const char *name
)
662 const struct regdomain
*dp
;
664 LIST_FOREACH(dp
, &rdp
->domains
, next
) {
665 if (strcasecmp(dp
->name
, name
) == 0)
672 * Lookup a country by ISO country code.
674 const struct country
*
675 lib80211_country_findbycc(const struct regdata
*rdp
, enum ISOCountryCode cc
)
677 const struct country
*cp
;
679 LIST_FOREACH(cp
, &rdp
->countries
, next
) {
687 * Lookup a country by ISO/long name.
689 const struct country
*
690 lib80211_country_findbyname(const struct regdata
*rdp
, const char *name
)
692 const struct country
*cp
;
696 LIST_FOREACH(cp
, &rdp
->countries
, next
) {
697 if (strcasecmp(cp
->isoname
, name
) == 0)
700 LIST_FOREACH(cp
, &rdp
->countries
, next
) {
701 if (strncasecmp(cp
->name
, name
, len
) == 0)