blob: bf36cea422006df02b5f47372d8385b0c048451b [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package regdb supports parsing the regulatory database used by the Linux kernel's WiFi framework.
package regdb
import (
"bytes"
"encoding/binary"
"io/ioutil"
"unicode"
"chromiumos/tast/errors"
)
const (
regdbPath = "/lib/firmware/regulatory.db"
// NB: the regulatory database layout is not documented, but it serves as a contract between the "wireless-regdb" (regulatory.db)
// project and the kernel. Its layout was determined from the source code.
headerMagic = 0x52474442 // RGDB
headerVersion = 20
)
// Country represents a country entry in the regulatory database.
type Country struct {
Alpha string
}
// RegulatoryDB holds country info from the WiFi regulatory database.
type RegulatoryDB struct {
Countries []*Country
}
func parseHeader(r *bytes.Reader) error {
var header struct {
Magic uint32
Version uint32
}
if err := binary.Read(r, binary.BigEndian, &header); err != nil {
return err
}
if header.Magic != headerMagic {
return errors.Errorf("invalid regdb magic: %#x", header.Magic)
}
if header.Version != headerVersion {
return errors.Errorf("invalid regdb version: %d", header.Version)
}
return nil
}
func parseCountry(r *bytes.Reader) (c *Country, done bool, err error) {
var country struct {
Alpha [2]byte
CollPtr uint16
}
if err = binary.Read(r, binary.BigEndian, &country); err != nil {
err = errors.Wrap(err, "failed reading country")
return
}
if country.CollPtr == 0 {
done = true
return
}
for i := 0; i < len(country.Alpha); i++ {
b := rune(country.Alpha[i])
if (!unicode.IsLetter(b) && !unicode.IsNumber(b)) || b < 0 || b > 127 {
err = errors.Errorf("invalid country string: %v", country.Alpha)
return
}
}
str := string(country.Alpha[:])
if len(str) != 2 {
err = errors.Errorf("invalid country string: %q", str)
return
}
c = &Country{
Alpha: str,
}
return
}
func parseCountries(r *bytes.Reader) ([]*Country, error) {
var countries []*Country
for {
country, done, err := parseCountry(r)
if err != nil {
return nil, err
} else if done {
return countries, nil
}
countries = append(countries, country)
}
}
func newRegulatoryDB(b []byte) (*RegulatoryDB, error) {
r := bytes.NewReader(b)
if err := parseHeader(r); err != nil {
return nil, err
}
countries, err := parseCountries(r)
if err != nil {
return nil, err
}
return &RegulatoryDB{
Countries: countries,
}, nil
}
// NewRegulatoryDB retrieves and parses the system's WiFi regulatory database.
func NewRegulatoryDB() (*RegulatoryDB, error) {
b, err := ioutil.ReadFile(regdbPath)
if err != nil {
return nil, err
}
return newRegulatoryDB(b)
}