5 kx #include <errno.h>
5 kx #include <stdio.h>
5 kx #include <arpa/inet.h>
5 kx #include <sys/types.h>
5 kx #include <dirent.h>
5 kx #include <sys/stat.h>
5 kx #include <stdlib.h>
5 kx #include <sys/mman.h>
5 kx #include <fcntl.h>
5 kx #include <stdbool.h>
5 kx #include <unistd.h>
5 kx #include <string.h>
5 kx #include <limits.h>
5 kx
5 kx #include <arpa/inet.h> /* ntohl */
5 kx
5 kx #include "reglib.h"
5 kx #include "regdb.h"
5 kx
5 kx #ifdef USE_OPENSSL
5 kx #include <openssl/objects.h>
5 kx #include <openssl/rsa.h>
5 kx #include <openssl/sha.h>
5 kx #include <openssl/pem.h>
5 kx #include <openssl/bn.h>
5 kx #endif
5 kx
5 kx #ifdef USE_GCRYPT
5 kx #include <gcrypt.h>
5 kx #endif
5 kx
5 kx #include "reglib.h"
5 kx
5 kx #ifdef USE_OPENSSL
5 kx #include "keys-ssl.c"
5 kx #endif
5 kx
5 kx #ifdef USE_GCRYPT
5 kx #include "keys-gcrypt.c"
5 kx #endif
5 kx
5 kx int debug = 0;
5 kx
5 kx void *
5 kx reglib_get_file_ptr(uint8_t *db, size_t dblen, size_t structlen, uint32_t ptr)
5 kx {
5 kx uint32_t p = ntohl(ptr);
5 kx
5 kx if (structlen > dblen) {
5 kx fprintf(stderr, "Invalid database file, too short!\n");
5 kx exit(3);
5 kx }
5 kx
5 kx if (p > dblen - structlen) {
5 kx fprintf(stderr, "Invalid database file, bad pointer!\n");
5 kx exit(3);
5 kx }
5 kx
5 kx return (void *)(db + p);
5 kx }
5 kx
5 kx static size_t
5 kx reglib_array_len(size_t baselen, unsigned int elemcount, size_t elemlen)
5 kx {
5 kx if (elemcount > (SIZE_MAX - baselen) / elemlen) {
5 kx fprintf(stderr, "Invalid database file, count too large!\n");
5 kx exit(3);
5 kx }
5 kx
5 kx return baselen + elemcount * elemlen;
5 kx }
5 kx
5 kx /*
5 kx * reglib_verify_db_signature():
5 kx *
5 kx * Checks the validity of the signature found on the regulatory
5 kx * database against the array 'keys'. Returns 1 if there exists
5 kx * at least one key in the array such that the signature is valid
5 kx * against that key; 0 otherwise.
5 kx */
5 kx
5 kx #ifdef USE_OPENSSL
5 kx int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
5 kx {
5 kx RSA *rsa = NULL;
5 kx BIGNUM *rsa_e = NULL, *rsa_n = NULL;
5 kx uint8_t hash[SHA_DIGEST_LENGTH];
5 kx unsigned int i;
5 kx int ok = 0;
5 kx DIR *pubkey_dir;
5 kx struct dirent *nextfile;
5 kx FILE *keyfile;
5 kx char filename[PATH_MAX];
5 kx
5 kx if (SHA1(db, dblen, hash) != hash) {
5 kx fprintf(stderr, "Failed to calculate SHA1 sum.\n");
5 kx goto out;
5 kx }
5 kx
5 kx for (i = 0; (i < sizeof(keys)/sizeof(keys[0])) && (!ok); i++) {
5 kx rsa = RSA_new();
5 kx if (!rsa) {
5 kx fprintf(stderr, "Failed to create RSA key.\n");
5 kx goto out;
5 kx }
5 kx
5 kx rsa_e = BN_bin2bn(keys[i].e, keys[i].len_e, NULL);
5 kx if (!rsa_e) {
5 kx fprintf(stderr, "Failed to convert value for RSA e.\n");
5 kx goto out;
5 kx }
5 kx rsa_n = BN_bin2bn(keys[i].n, keys[i].len_n, NULL);
5 kx if (!rsa_n) {
5 kx fprintf(stderr, "Failed to convert value for RSA n.\n");
5 kx goto out;
5 kx }
5 kx
5 kx #if OPENSSL_VERSION_NUMBER < 0x10100000L
5 kx rsa->e = rsa_e;
5 kx rsa->n = rsa_n;
5 kx #else
5 kx if (RSA_set0_key(rsa, rsa_n, rsa_e, NULL) != 1) {
5 kx fprintf(stderr, "Failed to set RSA key.\n");
5 kx goto out;
5 kx }
5 kx #endif
5 kx /* BIGNUMs now owned by the RSA object */
5 kx rsa_e = NULL;
5 kx rsa_n = NULL;
5 kx
5 kx ok = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH,
5 kx db + dblen, siglen, rsa) == 1;
5 kx
5 kx RSA_free(rsa);
5 kx rsa = NULL;
5 kx }
5 kx if (!ok && (pubkey_dir = opendir(PUBKEY_DIR))) {
5 kx while (!ok && (nextfile = readdir(pubkey_dir))) {
5 kx snprintf(filename, PATH_MAX, "%s/%s", PUBKEY_DIR,
5 kx nextfile->d_name);
5 kx if ((keyfile = fopen(filename, "rb"))) {
5 kx rsa = PEM_read_RSA_PUBKEY(keyfile,
5 kx NULL, NULL, NULL);
5 kx if (rsa)
5 kx ok = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH,
5 kx db + dblen, siglen, rsa) == 1;
5 kx RSA_free(rsa);
5 kx rsa = NULL;
5 kx fclose(keyfile);
5 kx }
5 kx }
5 kx closedir(pubkey_dir);
5 kx }
5 kx
5 kx if (!ok)
5 kx fprintf(stderr, "Database signature verification failed.\n");
5 kx
5 kx out:
5 kx RSA_free(rsa);
5 kx BN_free(rsa_e);
5 kx BN_free(rsa_n);
5 kx return ok;
5 kx }
5 kx #endif /* USE_OPENSSL */
5 kx
5 kx #ifdef USE_GCRYPT
5 kx int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
5 kx {
5 kx gcry_mpi_t mpi_e, mpi_n;
5 kx gcry_sexp_t rsa, signature, data;
5 kx uint8_t hash[20];
5 kx unsigned int i;
5 kx int ok = 0;
5 kx
5 kx /* initialise */
5 kx gcry_check_version(NULL);
5 kx
5 kx /* hash the db */
5 kx gcry_md_hash_buffer(GCRY_MD_SHA1, hash, db, dblen);
5 kx
5 kx if (gcry_sexp_build(&data, NULL, "(data (flags pkcs1) (hash sha1 %b))",
5 kx 20, hash)) {
5 kx fprintf(stderr, "Failed to build data S-expression.\n");
5 kx return ok;
5 kx }
5 kx
5 kx if (gcry_sexp_build(&signature, NULL, "(sig-val (rsa (s %b)))",
5 kx siglen, db + dblen)) {
5 kx fprintf(stderr, "Failed to build signature S-expression.\n");
5 kx gcry_sexp_release(data);
5 kx return ok;
5 kx }
5 kx
5 kx for (i = 0; (i < sizeof(keys)/sizeof(keys[0])) && (!ok); i++) {
5 kx if (gcry_mpi_scan(&mpi_e, GCRYMPI_FMT_USG,
5 kx keys[i].e, keys[i].len_e, NULL) ||
5 kx gcry_mpi_scan(&mpi_n, GCRYMPI_FMT_USG,
5 kx keys[i].n, keys[i].len_n, NULL)) {
5 kx fprintf(stderr, "Failed to convert numbers.\n");
5 kx goto out;
5 kx }
5 kx
5 kx if (gcry_sexp_build(&rsa, NULL,
5 kx "(public-key (rsa (n %m) (e %m)))",
5 kx mpi_n, mpi_e)) {
5 kx fprintf(stderr, "Failed to build RSA S-expression.\n");
5 kx gcry_mpi_release(mpi_e);
5 kx gcry_mpi_release(mpi_n);
5 kx goto out;
5 kx }
5 kx
5 kx ok = gcry_pk_verify(signature, data, rsa) == 0;
5 kx gcry_mpi_release(mpi_e);
5 kx gcry_mpi_release(mpi_n);
5 kx gcry_sexp_release(rsa);
5 kx }
5 kx
5 kx if (!ok)
5 kx fprintf(stderr, "Database signature verification failed.\n");
5 kx
5 kx out:
5 kx gcry_sexp_release(data);
5 kx gcry_sexp_release(signature);
5 kx return ok;
5 kx }
5 kx #endif /* USE_GCRYPT */
5 kx
5 kx #if !defined(USE_OPENSSL) && !defined(USE_GCRYPT)
5 kx int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
5 kx {
5 kx return 1;
5 kx }
5 kx #endif
5 kx
5 kx const struct reglib_regdb_ctx *reglib_malloc_regdb_ctx(const char *regdb_file)
5 kx {
5 kx struct regdb_file_header *header;
5 kx struct reglib_regdb_ctx *ctx;
5 kx
5 kx ctx = malloc(sizeof(struct reglib_regdb_ctx));
5 kx if (!ctx)
5 kx return NULL;
5 kx
5 kx memset(ctx, 0, sizeof(struct reglib_regdb_ctx));
5 kx
5 kx ctx->fd = open(regdb_file, O_RDONLY);
5 kx
5 kx if (ctx->fd < 0) {
5 kx free(ctx);
5 kx return NULL;
5 kx }
5 kx
5 kx if (fstat(ctx->fd, &ctx->stat)) {
5 kx close(ctx->fd);
5 kx free(ctx);
5 kx return NULL;
5 kx }
5 kx
5 kx ctx->real_dblen = ctx->stat.st_size;
5 kx
5 kx ctx->db = mmap(NULL, ctx->real_dblen, PROT_READ,
5 kx MAP_PRIVATE, ctx->fd, 0);
5 kx if (ctx->db == MAP_FAILED) {
5 kx close(ctx->fd);
5 kx free(ctx);
5 kx return NULL;
5 kx }
5 kx
5 kx ctx->header = reglib_get_file_ptr(ctx->db, ctx->real_dblen,
5 kx sizeof(struct regdb_file_header),
5 kx 0);
5 kx header = ctx->header;
5 kx
5 kx if (ntohl(header->magic) != REGDB_MAGIC)
5 kx goto err_out;
5 kx
5 kx if (ntohl(header->version) != REGDB_VERSION)
5 kx goto err_out;
5 kx
5 kx ctx->siglen = ntohl(header->signature_length);
5 kx
5 kx if (ctx->siglen > ctx->real_dblen - sizeof(*header))
5 kx goto err_out;
5 kx
5 kx /* The actual dblen does not take into account the signature */
5 kx ctx->dblen = ctx->real_dblen - ctx->siglen;
5 kx
5 kx /* verify signature */
5 kx if (!reglib_verify_db_signature(ctx->db, ctx->dblen, ctx->siglen))
5 kx goto err_out;
5 kx
5 kx ctx->verified = true;
5 kx ctx->num_countries = ntohl(header->reg_country_num);
5 kx ctx->countries = reglib_get_file_ptr(ctx->db,
5 kx ctx->dblen,
5 kx sizeof(struct regdb_file_reg_country) * ctx->num_countries,
5 kx header->reg_country_ptr);
5 kx return ctx;
5 kx
5 kx err_out:
5 kx close(ctx->fd);
5 kx munmap(ctx->db, ctx->real_dblen);
5 kx free(ctx);
5 kx return NULL;
5 kx }
5 kx
5 kx void reglib_free_regdb_ctx(const struct reglib_regdb_ctx *regdb_ctx)
5 kx {
5 kx struct reglib_regdb_ctx *ctx;
5 kx
5 kx if (!regdb_ctx)
5 kx return;
5 kx
5 kx ctx = (struct reglib_regdb_ctx *) regdb_ctx;
5 kx
5 kx memset(ctx, 0, sizeof(struct reglib_regdb_ctx));
5 kx close(ctx->fd);
5 kx munmap(ctx->db, ctx->real_dblen);
5 kx free(ctx);
5 kx }
5 kx
5 kx static void reg_rule2rd(uint8_t *db, size_t dblen,
5 kx uint32_t ruleptr, struct ieee80211_reg_rule *rd_reg_rule)
5 kx {
5 kx struct regdb_file_reg_rule *rule;
5 kx struct regdb_file_freq_range *freq;
5 kx struct regdb_file_power_rule *power;
5 kx
5 kx struct ieee80211_freq_range *rd_freq_range = &rd_reg_rule->freq_range;
5 kx struct ieee80211_power_rule *rd_power_rule = &rd_reg_rule->power_rule;
5 kx
5 kx rule = reglib_get_file_ptr(db, dblen, sizeof(*rule), ruleptr);
5 kx freq = reglib_get_file_ptr(db, dblen, sizeof(*freq), rule->freq_range_ptr);
5 kx power = reglib_get_file_ptr(db, dblen, sizeof(*power), rule->power_rule_ptr);
5 kx
5 kx rd_freq_range->start_freq_khz = ntohl(freq->start_freq);
5 kx rd_freq_range->end_freq_khz = ntohl(freq->end_freq);
5 kx rd_freq_range->max_bandwidth_khz = ntohl(freq->max_bandwidth);
5 kx
5 kx rd_power_rule->max_antenna_gain = ntohl(power->max_antenna_gain);
5 kx rd_power_rule->max_eirp = ntohl(power->max_eirp);
5 kx
5 kx rd_reg_rule->flags = ntohl(rule->flags);
5 kx
5 kx if (rd_reg_rule->flags & RRF_NO_IR_ALL)
5 kx rd_reg_rule->flags |= RRF_NO_IR_ALL;
5 kx }
5 kx
5 kx /* Converts a file regdomain to ieee80211_regdomain, easier to manage */
5 kx const static struct ieee80211_regdomain *
5 kx country2rd(const struct reglib_regdb_ctx *ctx,
5 kx struct regdb_file_reg_country *country)
5 kx {
5 kx struct regdb_file_reg_rules_collection *rcoll;
5 kx struct ieee80211_regdomain *rd;
5 kx unsigned int i, num_rules;
5 kx size_t size_of_rd;
5 kx
5 kx rcoll = reglib_get_file_ptr(ctx->db, ctx->dblen, sizeof(*rcoll),
5 kx country->reg_collection_ptr);
5 kx num_rules = ntohl(rcoll->reg_rule_num);
5 kx /* re-get pointer with sanity checking for num_rules */
5 kx rcoll = reglib_get_file_ptr(ctx->db, ctx->dblen,
5 kx reglib_array_len(sizeof(*rcoll), num_rules,
5 kx sizeof(uint32_t)),
5 kx country->reg_collection_ptr);
5 kx
5 kx size_of_rd = reglib_array_len(sizeof(struct ieee80211_regdomain),
5 kx num_rules,
5 kx sizeof(struct ieee80211_reg_rule));
5 kx
5 kx rd = malloc(size_of_rd);
5 kx if (!rd)
5 kx return NULL;
5 kx
5 kx memset(rd, 0, size_of_rd);
5 kx
5 kx rd->alpha2[0] = country->alpha2[0];
5 kx rd->alpha2[1] = country->alpha2[1];
5 kx rd->dfs_region = country->creqs & 0x3;
5 kx rd->n_reg_rules = num_rules;
5 kx
5 kx for (i = 0; i < num_rules; i++) {
5 kx reg_rule2rd(ctx->db, ctx->dblen, rcoll->reg_rule_ptrs[i],
5 kx &rd->reg_rules[i]);
5 kx }
5 kx
5 kx return rd;
5 kx }
5 kx
5 kx const struct ieee80211_regdomain *
5 kx reglib_get_rd_idx(unsigned int idx, const struct reglib_regdb_ctx *ctx)
5 kx {
5 kx struct regdb_file_reg_country *country;
5 kx
5 kx if (!ctx)
5 kx return NULL;
5 kx
5 kx if (idx >= ctx->num_countries)
5 kx return NULL;
5 kx
5 kx country = ctx->countries + idx;
5 kx
5 kx return country2rd(ctx, country);
5 kx }
5 kx
5 kx const struct ieee80211_regdomain *
5 kx reglib_get_rd_alpha2(const char *alpha2, const char *file)
5 kx {
5 kx const struct reglib_regdb_ctx *ctx;
5 kx const struct ieee80211_regdomain *rd = NULL;
5 kx struct regdb_file_reg_country *country;
5 kx bool found_country = false;
5 kx unsigned int i;
5 kx
5 kx ctx = reglib_malloc_regdb_ctx(file);
5 kx if (!ctx)
5 kx return NULL;
5 kx
5 kx for (i = 0; i < ctx->num_countries; i++) {
5 kx country = ctx->countries + i;
5 kx if (memcmp(country->alpha2, alpha2, 2) == 0) {
5 kx found_country = 1;
5 kx break;
5 kx }
5 kx }
5 kx
5 kx if (!found_country)
5 kx goto out;
5 kx
5 kx rd = country2rd(ctx, country);
5 kx if (!rd)
5 kx goto out;
5 kx
5 kx out:
5 kx reglib_free_regdb_ctx(ctx);
5 kx return rd;
5 kx }
5 kx
5 kx /* Sanity check on a regulatory rule */
5 kx static int is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
5 kx {
5 kx const struct ieee80211_freq_range *freq_range = &rule->freq_range;
5 kx uint32_t freq_diff;
5 kx
5 kx if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0)
5 kx return 0;
5 kx
5 kx if (freq_range->start_freq_khz > freq_range->end_freq_khz)
5 kx return 0;
5 kx
5 kx freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
5 kx
5 kx if (freq_range->end_freq_khz <= freq_range->start_freq_khz ||
5 kx freq_range->max_bandwidth_khz > freq_diff)
5 kx return 0;
5 kx
5 kx return 1;
5 kx }
5 kx
5 kx int reglib_is_valid_rd(const struct ieee80211_regdomain *rd)
5 kx {
5 kx const struct ieee80211_reg_rule *reg_rule = NULL;
5 kx unsigned int i;
5 kx
5 kx if (!rd->n_reg_rules)
5 kx return 0;
5 kx
5 kx for (i = 0; i < rd->n_reg_rules; i++) {
5 kx reg_rule = &rd->reg_rules[i];
5 kx if (!is_valid_reg_rule(reg_rule))
5 kx return 0;
5 kx }
5 kx return 1;
5 kx }
5 kx
5 kx static int reg_rules_union(const struct ieee80211_reg_rule *rule1,
5 kx const struct ieee80211_reg_rule *rule2,
5 kx struct ieee80211_reg_rule *union_rule)
5 kx {
5 kx const struct ieee80211_freq_range *freq_range1, *freq_range2;
5 kx struct ieee80211_freq_range *freq_range;
5 kx const struct ieee80211_power_rule *power_rule1, *power_rule2;
5 kx struct ieee80211_power_rule *power_rule;
5 kx
5 kx freq_range1 = &rule1->freq_range;
5 kx freq_range2 = &rule2->freq_range;
5 kx freq_range = &union_rule->freq_range;
5 kx
5 kx power_rule1 = &rule1->power_rule;
5 kx power_rule2 = &rule2->power_rule;
5 kx power_rule = &union_rule->power_rule;
5 kx
5 kx
5 kx if (freq_range1->end_freq_khz < freq_range2->start_freq_khz)
5 kx return -EINVAL;
5 kx if (freq_range2->end_freq_khz < freq_range1->start_freq_khz)
5 kx return -EINVAL;
5 kx
5 kx freq_range->start_freq_khz = reglib_min(freq_range1->start_freq_khz,
5 kx freq_range2->start_freq_khz);
5 kx freq_range->end_freq_khz = reglib_max(freq_range1->end_freq_khz,
5 kx freq_range2->end_freq_khz);
5 kx freq_range->max_bandwidth_khz = reglib_max(freq_range1->max_bandwidth_khz,
5 kx freq_range2->max_bandwidth_khz);
5 kx
5 kx power_rule->max_eirp = reglib_max(power_rule1->max_eirp,
5 kx power_rule2->max_eirp);
5 kx power_rule->max_antenna_gain = reglib_max(power_rule1->max_antenna_gain,
5 kx power_rule2->max_antenna_gain);
5 kx
5 kx union_rule->flags = rule1->flags | rule2->flags;
5 kx
5 kx if (!is_valid_reg_rule(union_rule))
5 kx return -EINVAL;
5 kx
5 kx return 0;
5 kx }
5 kx
5 kx /*
5 kx * Helper for reglib_intersect_rds(), this does the real
5 kx * mathematical intersection fun
5 kx */
5 kx static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,
5 kx const struct ieee80211_reg_rule *rule2,
5 kx struct ieee80211_reg_rule *intersected_rule)
5 kx {
5 kx const struct ieee80211_freq_range *freq_range1, *freq_range2;
5 kx struct ieee80211_freq_range *freq_range;
5 kx const struct ieee80211_power_rule *power_rule1, *power_rule2;
5 kx struct ieee80211_power_rule *power_rule;
5 kx uint32_t freq_diff;
5 kx
5 kx freq_range1 = &rule1->freq_range;
5 kx freq_range2 = &rule2->freq_range;
5 kx freq_range = &intersected_rule->freq_range;
5 kx
5 kx power_rule1 = &rule1->power_rule;
5 kx power_rule2 = &rule2->power_rule;
5 kx power_rule = &intersected_rule->power_rule;
5 kx
5 kx freq_range->start_freq_khz = reglib_max(freq_range1->start_freq_khz,
5 kx freq_range2->start_freq_khz);
5 kx freq_range->end_freq_khz = reglib_min(freq_range1->end_freq_khz,
5 kx freq_range2->end_freq_khz);
5 kx freq_range->max_bandwidth_khz = reglib_min(freq_range1->max_bandwidth_khz,
5 kx freq_range2->max_bandwidth_khz);
5 kx
5 kx freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
5 kx if (freq_range->max_bandwidth_khz > freq_diff)
5 kx freq_range->max_bandwidth_khz = freq_diff;
5 kx
5 kx power_rule->max_eirp = reglib_min(power_rule1->max_eirp,
5 kx power_rule2->max_eirp);
5 kx power_rule->max_antenna_gain = reglib_min(power_rule1->max_antenna_gain,
5 kx power_rule2->max_antenna_gain);
5 kx
5 kx intersected_rule->flags = rule1->flags | rule2->flags;
5 kx
5 kx if (!is_valid_reg_rule(intersected_rule))
5 kx return -EINVAL;
5 kx
5 kx return 0;
5 kx }
5 kx
5 kx /**
5 kx * reglib_intersect_rds - do the intersection between two regulatory domains
5 kx * @rd1: first regulatory domain
5 kx * @rd2: second regulatory domain
5 kx *
5 kx * Use this function to get the intersection between two regulatory domains.
5 kx * Once completed we will mark the alpha2 for the rd as intersected, "98",
5 kx * as no one single alpha2 can represent this regulatory domain.
5 kx *
5 kx * Returns a pointer to the regulatory domain structure which will hold the
5 kx * resulting intersection of rules between rd1 and rd2. We will
5 kx * malloc() this structure for you.
5 kx */
5 kx struct ieee80211_regdomain *
5 kx reglib_intersect_rds(const struct ieee80211_regdomain *rd1,
5 kx const struct ieee80211_regdomain *rd2)
5 kx {
5 kx int r;
5 kx size_t size_of_regd;
5 kx unsigned int x, y;
5 kx unsigned int num_rules = 0, rule_idx = 0;
5 kx const struct ieee80211_reg_rule *rule1, *rule2;
5 kx struct ieee80211_reg_rule *intersected_rule;
5 kx struct ieee80211_regdomain *rd;
5 kx /* This is just a dummy holder to help us count */
5 kx struct ieee80211_reg_rule irule;
5 kx
5 kx /* Uses the stack temporarily for counter arithmetic */
5 kx intersected_rule = &irule;
5 kx
5 kx memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule));
5 kx
5 kx if (!rd1 || !rd2)
5 kx return NULL;
5 kx
5 kx /* First we get a count of the rules we'll need, then we actually
5 kx * build them. This is to so we can malloc() and free() a
5 kx * regdomain once. The reason we use reg_rules_intersect() here
5 kx * is it will return -EINVAL if the rule computed makes no sense.
5 kx * All rules that do check out OK are valid. */
5 kx
5 kx for (x = 0; x < rd1->n_reg_rules; x++) {
5 kx rule1 = &rd1->reg_rules[x];
5 kx for (y = 0; y < rd2->n_reg_rules; y++) {
5 kx rule2 = &rd2->reg_rules[y];
5 kx if (!reg_rules_intersect(rule1, rule2,
5 kx intersected_rule))
5 kx num_rules++;
5 kx memset(intersected_rule, 0,
5 kx sizeof(struct ieee80211_reg_rule));
5 kx }
5 kx }
5 kx
5 kx if (!num_rules)
5 kx return NULL;
5 kx
5 kx size_of_regd = reglib_array_len(sizeof(struct ieee80211_regdomain),
5 kx num_rules + 1,
5 kx sizeof(struct ieee80211_reg_rule));
5 kx
5 kx rd = malloc(size_of_regd);
5 kx if (!rd)
5 kx return NULL;
5 kx
5 kx memset(rd, 0, size_of_regd);
5 kx
5 kx for (x = 0; x < rd1->n_reg_rules; x++) {
5 kx rule1 = &rd1->reg_rules[x];
5 kx for (y = 0; y < rd2->n_reg_rules; y++) {
5 kx rule2 = &rd2->reg_rules[y];
5 kx /* This time around instead of using the stack lets
5 kx * write to the target rule directly saving ourselves
5 kx * a memcpy() */
5 kx intersected_rule = &rd->reg_rules[rule_idx];
5 kx r = reg_rules_intersect(rule1, rule2,
5 kx intersected_rule);
5 kx if (r)
5 kx continue;
5 kx rule_idx++;
5 kx }
5 kx }
5 kx
5 kx if (rule_idx != num_rules) {
5 kx free(rd);
5 kx return NULL;
5 kx }
5 kx
5 kx rd->n_reg_rules = num_rules;
5 kx rd->alpha2[0] = '9';
5 kx rd->alpha2[1] = '9';
5 kx
5 kx return rd;
5 kx }
5 kx
5 kx const struct ieee80211_regdomain *
5 kx reglib_intersect_regdb(const struct reglib_regdb_ctx *ctx)
5 kx {
5 kx const struct ieee80211_regdomain *rd;
5 kx struct ieee80211_regdomain *prev_rd_intsct = NULL, *rd_intsct = NULL;
5 kx int intersected = 0;
5 kx unsigned int idx = 0;
5 kx
5 kx if (!ctx)
5 kx return NULL;
5 kx
5 kx reglib_for_each_country(rd, idx, ctx) {
5 kx if (reglib_is_world_regdom((const char *) rd->alpha2)) {
5 kx free((struct ieee80211_regdomain *) rd);
5 kx continue;
5 kx }
5 kx
5 kx if (!prev_rd_intsct) {
5 kx prev_rd_intsct = (struct ieee80211_regdomain *) rd;
5 kx continue;
5 kx }
5 kx
5 kx if (rd_intsct) {
5 kx free(prev_rd_intsct);
5 kx prev_rd_intsct = (struct ieee80211_regdomain *) rd_intsct;
5 kx }
5 kx
5 kx rd_intsct = reglib_intersect_rds(prev_rd_intsct, rd);
5 kx if (!rd_intsct) {
5 kx free(prev_rd_intsct);
5 kx free((struct ieee80211_regdomain *) rd);
5 kx return NULL;
5 kx }
5 kx
5 kx intersected++;
5 kx free((struct ieee80211_regdomain *) rd);
5 kx }
5 kx
5 kx if (!idx)
5 kx return NULL;
5 kx
5 kx if (intersected <= 0) {
5 kx rd_intsct = prev_rd_intsct;
5 kx prev_rd_intsct = NULL;
5 kx if (idx > 1) {
5 kx free(rd_intsct);
5 kx return NULL;
5 kx }
5 kx }
5 kx
5 kx if (prev_rd_intsct)
5 kx free(prev_rd_intsct);
5 kx
5 kx return rd_intsct;
5 kx }
5 kx
5 kx static const char *dfs_domain_name(enum regdb_dfs_regions region)
5 kx {
5 kx switch (region) {
5 kx case REGDB_DFS_UNSET:
5 kx return "DFS-UNSET";
5 kx case REGDB_DFS_FCC:
5 kx return "DFS-FCC";
5 kx case REGDB_DFS_ETSI:
5 kx return "DFS-ETSI";
5 kx case REGDB_DFS_JP:
5 kx return "DFS-JP";
5 kx default:
5 kx return "DFS-invalid";
5 kx }
5 kx }
5 kx
5 kx static void print_reg_rule(const struct ieee80211_reg_rule *rule)
5 kx {
5 kx const struct ieee80211_freq_range *freq;
5 kx const struct ieee80211_power_rule *power;
5 kx
5 kx freq = &rule->freq_range;
5 kx power = &rule->power_rule;
5 kx
5 kx printf("\t(%.3f - %.3f @ %.3f), ",
5 kx ((float)(freq->start_freq_khz))/1000.0,
5 kx ((float)(freq->end_freq_khz))/1000.0,
5 kx ((float)(freq->max_bandwidth_khz))/1000.0);
5 kx
5 kx printf("(");
5 kx
5 kx if (power->max_eirp)
5 kx printf("%.2f)", ((float)(power->max_eirp)/100.0));
5 kx else
5 kx printf("N/A)");
5 kx
5 kx if (rule->dfs_cac_ms)
5 kx printf(", (%u)", rule->dfs_cac_ms);
5 kx else
5 kx printf(", (N/A)");
5 kx
5 kx if (rule->flags & RRF_NO_OFDM)
5 kx printf(", NO-OFDM");
5 kx if (rule->flags & RRF_NO_CCK)
5 kx printf(", NO-CCK");
5 kx if (rule->flags & RRF_NO_INDOOR)
5 kx printf(", NO-INDOOR");
5 kx if (rule->flags & RRF_NO_OUTDOOR)
5 kx printf(", NO-OUTDOOR");
5 kx if (rule->flags & RRF_DFS)
5 kx printf(", DFS");
5 kx if (rule->flags & RRF_PTP_ONLY)
5 kx printf(", PTP-ONLY");
5 kx if (rule->flags & RRF_PTMP_ONLY)
5 kx printf(", PTMP-ONLY");
5 kx if (rule->flags & RRF_NO_IR_ALL)
5 kx printf(", NO-IR");
5 kx if (rule->flags & RRF_AUTO_BW)
5 kx printf(", AUTO-BW");
5 kx
5 kx printf("\n");
5 kx }
5 kx
5 kx void reglib_print_regdom(const struct ieee80211_regdomain *rd)
5 kx {
5 kx unsigned int i;
5 kx printf("country %.2s: %s\n", rd->alpha2,
5 kx dfs_domain_name(rd->dfs_region));
5 kx for (i = 0; i < rd->n_reg_rules; i++)
5 kx print_reg_rule(&rd->reg_rules[i]);
5 kx printf("\n");
5 kx }
5 kx
5 kx static unsigned int reglib_parse_dfs_region(char *dfs_region)
5 kx {
5 kx if (!dfs_region)
5 kx return REGDB_DFS_UNSET;
5 kx
5 kx if (strstr(dfs_region, "DFS-FCC"))
5 kx return REGDB_DFS_FCC;
5 kx if (strstr(dfs_region, "DFS-ETSI"))
5 kx return REGDB_DFS_ETSI;
5 kx if (strstr(dfs_region, "DFS-JP"))
5 kx return REGDB_DFS_JP;
5 kx return REGDB_DFS_UNSET;
5 kx }
5 kx
5 kx static uint32_t reglib_parse_rule_flag(char *flag_s)
5 kx {
5 kx uint32_t flags = 0;
5 kx
5 kx if (strstr(flag_s, "NO-OFDM"))
5 kx flags |= RRF_NO_OFDM;
5 kx if (strstr(flag_s, "NO-CCK"))
5 kx flags |= RRF_NO_CCK;
5 kx if (strstr(flag_s, "NO-INDOOR"))
5 kx flags |= RRF_NO_INDOOR;
5 kx if (strstr(flag_s, "NO-OUTDOOR"))
5 kx flags |= RRF_NO_OUTDOOR;
5 kx if (strstr(flag_s, "DFS"))
5 kx flags |= RRF_DFS;
5 kx if (strstr(flag_s, "PTP-ONLY"))
5 kx flags |= RRF_PTP_ONLY;
5 kx if (strstr(flag_s, "PTMP-ONLY"))
5 kx flags |= RRF_PTMP_ONLY;
5 kx if (strstr(flag_s, "NO-IR"))
5 kx flags |= RRF_NO_IR;
5 kx if (strstr(flag_s, "AUTO-BW"))
5 kx flags |= RRF_AUTO_BW;
5 kx
5 kx return flags;
5 kx }
5 kx
5 kx static int reglib_parse_rule(FILE *fp, struct ieee80211_reg_rule *reg_rule)
5 kx {
5 kx char line[1024];
5 kx char *line_p;
5 kx int hits, r = 0;
5 kx float start_freq_khz, end_freq_khz, max_bandwidth_khz, max_eirp;
5 kx unsigned int dfs_cac_ms = 0;
5 kx
5 kx memset(line, 0, sizeof(line));
5 kx line_p = fgets(line, sizeof(line), fp);
5 kx if (line_p != line)
5 kx return -EINVAL;
5 kx
5 kx /* First get start, end and bandwidth */
5 kx hits = sscanf(line_p, "\t(%f - %f @ %f),",
5 kx &start_freq_khz,
5 kx &end_freq_khz,
5 kx &max_bandwidth_khz);
5 kx
5 kx if (hits != 3)
5 kx return -EINVAL;
5 kx
5 kx reg_rule->freq_range.start_freq_khz =
5 kx REGLIB_MHZ_TO_KHZ(start_freq_khz);
5 kx reg_rule->freq_range.end_freq_khz =
5 kx REGLIB_MHZ_TO_KHZ(end_freq_khz);
5 kx reg_rule->freq_range.max_bandwidth_khz =
5 kx REGLIB_MHZ_TO_KHZ(max_bandwidth_khz);
5 kx
5 kx /* Next get eirp */
5 kx strsep(&line_p, ",");
5 kx if (!line_p) {
5 kx fprintf(stderr, "not found eirp in line: %s\n", line);
5 kx return -EINVAL;
5 kx }
5 kx
5 kx if (strstr(line_p, "mW")) {
5 kx hits = sscanf(line_p, " (%f mW)", &max_eirp);
5 kx if (hits != 1)
5 kx return -EINVAL;
5 kx reg_rule->power_rule.max_eirp =
5 kx REGLIB_MW_TO_MBM(max_eirp);
5 kx } else {
5 kx hits = sscanf(line_p, " (%f)", &max_eirp);
5 kx if (hits != 1)
5 kx return -EINVAL;
5 kx reg_rule->power_rule.max_eirp =
5 kx REGLIB_DBM_TO_MBM(max_eirp);
5 kx }
5 kx
5 kx /* Next get optional arguments (flags ...) */
5 kx strsep(&line_p, ",");
5 kx if (line_p) {
5 kx /* Check DFS CAC time */
5 kx hits = sscanf(line_p, " (%u)", &dfs_cac_ms);
5 kx if (hits == 1)
5 kx reg_rule->dfs_cac_ms = dfs_cac_ms;
5 kx
5 kx /* Check flags */
5 kx reg_rule->flags = reglib_parse_rule_flag(line_p);
5 kx }
5 kx
5 kx return r;
5 kx }
5 kx
5 kx static uint32_t
5 kx reglib_get_n_rules(FILE *fp, struct ieee80211_reg_rule *reg_rule)
5 kx {
5 kx uint32_t n_rules = 0;
5 kx int r;
5 kx bool save_debug = false;
5 kx
5 kx save_debug = debug;
5 kx debug = false;
5 kx
5 kx while (1) {
5 kx r = reglib_parse_rule(fp, reg_rule);
5 kx if (r != 0)
5 kx break;
5 kx n_rules++;
5 kx }
5 kx
5 kx debug = save_debug;
5 kx
5 kx return n_rules;
5 kx }
5 kx
5 kx static int reglib_parse_reg_rule(FILE *fp, struct ieee80211_reg_rule *reg_rule)
5 kx {
5 kx int r;
5 kx
5 kx while (1) {
5 kx r = reglib_parse_rule(fp, reg_rule);
5 kx if (r != 0)
5 kx continue;
5 kx return 0;
5 kx }
5 kx }
5 kx
5 kx static struct ieee80211_regdomain *
5 kx reglib_parse_rules(FILE *fp, struct ieee80211_regdomain *trd)
5 kx {
5 kx struct ieee80211_regdomain *rd;
5 kx struct ieee80211_reg_rule rule;
5 kx struct ieee80211_reg_rule *reg_rule;
5 kx fpos_t pos;
5 kx unsigned int i;
5 kx uint32_t size_of_regd = 0, num_rules = 0;
5 kx int r;
5 kx
5 kx memset(&rule, 0, sizeof(rule));
5 kx reg_rule = &rule;
5 kx
5 kx r = fgetpos(fp, &pos);
5 kx if (r != 0) {
5 kx fprintf(stderr, "fgetpos() failed: %s\n",
5 kx strerror(errno));
5 kx return NULL;
5 kx }
5 kx
5 kx num_rules = reglib_get_n_rules(fp, reg_rule);
5 kx if (!num_rules)
5 kx return NULL;
5 kx
5 kx size_of_regd = reglib_array_len(sizeof(struct ieee80211_regdomain),
5 kx num_rules + 1,
5 kx sizeof(struct ieee80211_reg_rule));
5 kx rd = malloc(size_of_regd);
5 kx if (!rd)
5 kx return NULL;
5 kx
5 kx memset(rd, 0, size_of_regd);
5 kx memcpy(rd, trd, sizeof(*trd));
5 kx
5 kx rd->n_reg_rules = num_rules;
5 kx
5 kx r = fsetpos(fp, &pos);
5 kx if (r != 0) {
5 kx fprintf(stderr, "fsetpos() failed: %s\n",
5 kx strerror(errno));
5 kx free(rd);
5 kx return NULL;
5 kx }
5 kx for (i = 0; i < num_rules; i++) {
5 kx struct ieee80211_reg_rule *rrule = &rd->reg_rules[i];
5 kx
5 kx if (reglib_parse_reg_rule(fp, rrule) != 0) {
5 kx fprintf(stderr, "rule parse failed\n");
5 kx free(rd);
5 kx return NULL;
5 kx }
5 kx }
5 kx return rd;
5 kx }
5 kx
5 kx static int reglib_parse_country_dfs(char *line, struct ieee80211_regdomain *rd)
5 kx {
5 kx char dfs_region_alpha[9];
5 kx char alpha2[2];
5 kx int hits;
5 kx
5 kx memset(rd, 0, sizeof(*rd));
5 kx memset(alpha2, 0, sizeof(alpha2));
5 kx memset(dfs_region_alpha, 0, sizeof(dfs_region_alpha));
5 kx
5 kx hits = sscanf(line, "country %2[a-zA-Z0-9]:%*[ ]%s\n",
5 kx alpha2,
5 kx dfs_region_alpha);
5 kx if (hits <= 0)
5 kx return -EINVAL;
5 kx
5 kx rd->alpha2[0] = alpha2[0];
5 kx rd->alpha2[1] = alpha2[1];
5 kx rd->dfs_region = reglib_parse_dfs_region(dfs_region_alpha);
5 kx
5 kx return 0;
5 kx }
5 kx
5 kx struct ieee80211_regdomain *__reglib_parse_country(FILE *fp)
5 kx {
5 kx struct ieee80211_regdomain *rd;
5 kx struct ieee80211_regdomain tmp_rd;
5 kx char line[1024];
5 kx char *line_p;
5 kx int r = 0;
5 kx
5 kx memset(&tmp_rd, 0, sizeof(tmp_rd));
5 kx memset(line, 0, sizeof(line));
5 kx
5 kx line_p = fgets(line, sizeof(line), fp);
5 kx
5 kx if (line_p != line) {
5 kx return NULL;
5 kx }
5 kx
5 kx /* Country */
5 kx r = reglib_parse_country_dfs(line_p, &tmp_rd);
5 kx if (r != 0) {
5 kx fprintf(stderr, "Invalid country line: %s", line);
5 kx return NULL;
5 kx }
5 kx
5 kx /* Rules */
5 kx rd = reglib_parse_rules(fp, &tmp_rd);
5 kx
5 kx return rd;
5 kx }
5 kx
5 kx static int reglib_find_next_country_stream(FILE *fp)
5 kx {
5 kx fpos_t prev_pos;
5 kx int r;
5 kx unsigned int i = 0;
5 kx
5 kx while(1) {
5 kx char line[1024];
5 kx char *line_p;
5 kx
5 kx r = fgetpos(fp, &prev_pos);
5 kx if (r != 0) {
5 kx fprintf(stderr, "fgetpos() failed: %s\n",
5 kx strerror(errno));
5 kx return r;
5 kx }
5 kx
5 kx memset(line, 0, sizeof(line));
5 kx
5 kx line_p = fgets(line, sizeof(line), fp);
5 kx if (line_p == line) {
5 kx if (strspn(line, "\n") == strlen(line)) {
5 kx i++;
5 kx continue;
5 kx }
5 kx if (strncmp(line, "country", 7) != 0)
5 kx continue;
5 kx r = fsetpos(fp, &prev_pos);
5 kx if (r != 0) {
5 kx fprintf(stderr, "fsetpos() failed: %s\n",
5 kx strerror(errno));
5 kx return r;
5 kx }
5 kx return 0;
5 kx } else
5 kx return EOF;
5 kx }
5 kx }
5 kx
5 kx struct ieee80211_regdomain *reglib_parse_country(FILE *fp)
5 kx {
5 kx int r;
5 kx
5 kx r = reglib_find_next_country_stream(fp);
5 kx if (r != 0)
5 kx return NULL;
5 kx return __reglib_parse_country(fp);
5 kx }
5 kx
5 kx FILE *reglib_create_parse_stream(FILE *f)
5 kx {
5 kx unsigned int lines = 0;
5 kx FILE *fp;
5 kx
5 kx fp = tmpfile();
5 kx if (errno) {
5 kx fprintf(stderr, "%s\n", strerror(errno));
5 kx return NULL;
5 kx }
5 kx
5 kx while(1) {
5 kx char line[1024];
5 kx char *line_p;
5 kx
5 kx line_p = fgets(line, sizeof(line), f);
5 kx if (line_p == line) {
5 kx if (strchr(line, '#') == NULL) {
5 kx fputs(line, fp);
5 kx lines++;
5 kx }
5 kx continue;
5 kx } else
5 kx break;
5 kx }
5 kx
5 kx rewind(fp);
5 kx fflush(fp);
5 kx
5 kx return fp;
5 kx }
5 kx
5 kx /*
5 kx * Just whatever for now, nothing formal, but note that as bands
5 kx * grow we'll want to make this a bit more formal somehow.
5 kx */
5 kx static uint32_t reglib_deduce_band(uint32_t start_freq_khz)
5 kx {
5 kx uint32_t freq_mhz = REGLIB_KHZ_TO_MHZ(start_freq_khz);
5 kx
5 kx if (freq_mhz >= 4000)
5 kx return 5;
5 kx if (freq_mhz > 2000 && freq_mhz < 4000)
5 kx return 2;
5 kx if (freq_mhz > 50000)
5 kx return 60;
5 kx return 1234;
5 kx }
5 kx
5 kx /*
5 kx * The idea behind a rule key is that if two rule keys share the
5 kx * same key they can be merged together if their frequencies overlap.
5 kx */
5 kx static uint64_t reglib_rule_key(struct ieee80211_reg_rule *reg_rule)
5 kx {
5 kx struct ieee80211_power_rule *power_rule;
5 kx struct ieee80211_freq_range *freq_range;
5 kx uint32_t band;
5 kx uint32_t key;
5 kx
5 kx freq_range = ®_rule->freq_range;
5 kx band = reglib_deduce_band(freq_range->start_freq_khz);
5 kx
5 kx power_rule = ®_rule->power_rule;
5 kx
5 kx key = ((power_rule->max_eirp ^ 0) << 0) ^
5 kx ((reg_rule->flags ^ 8) << 8) ^
5 kx ((band ^ 16) << 16);
5 kx
5 kx return key;
5 kx }
5 kx
5 kx struct reglib_optimize_map {
5 kx bool optimized;
5 kx uint32_t key;
5 kx };
5 kx
5 kx /* Does the provided rule suffice both of the other two */
5 kx static int reglib_opt_rule_fit(struct ieee80211_reg_rule *rule1,
5 kx struct ieee80211_reg_rule *rule2,
5 kx struct ieee80211_reg_rule *opt_rule)
5 kx {
5 kx struct ieee80211_reg_rule interesected_rule;
5 kx struct ieee80211_reg_rule *int_rule;
5 kx int r;
5 kx
5 kx memset(&interesected_rule, 0, sizeof(struct ieee80211_reg_rule));
5 kx int_rule = &interesected_rule;
5 kx
5 kx r = reg_rules_intersect(rule1, opt_rule, int_rule);
5 kx if (r != 0)
5 kx return r;
5 kx r = reg_rules_intersect(rule2, opt_rule, int_rule);
5 kx if (r != 0)
5 kx return r;
5 kx
5 kx return 0;
5 kx }
5 kx
5 kx static int reg_rule_optimize(struct ieee80211_reg_rule *rule1,
5 kx struct ieee80211_reg_rule *rule2,
5 kx struct ieee80211_reg_rule *opt_rule)
5 kx {
5 kx int r;
5 kx
5 kx r = reg_rules_union(rule1, rule2, opt_rule);
5 kx if (r != 0)
5 kx return r;
5 kx r = reglib_opt_rule_fit(rule1, rule2, opt_rule);
5 kx if (r != 0)
5 kx return r;
5 kx
5 kx return 0;
5 kx }
5 kx
5 kx /*
5 kx * Here's the math explanation:
5 kx *
5 kx * This takes each pivot frequency on the regulatory domain, computes
5 kx * the union between it each regulatory rule on the regulatory domain
5 kx * sequentially, and after that it tries to verify that the pivot frequency
5 kx * fits on it by computing an intersection between it and the union, if
5 kx * a rule exist as a possible intersection then we know the rule can be
5 kx * subset of the combination of the two frequency ranges (union) computed.
5 kx */
5 kx static unsigned int reg_rule_optimize_rd(struct ieee80211_regdomain *rd,
5 kx unsigned int rule_idx,
5 kx struct ieee80211_reg_rule *opt_rule,
5 kx struct reglib_optimize_map *opt_map)
5 kx {
5 kx unsigned int i;
5 kx struct ieee80211_reg_rule *rule1;
5 kx struct ieee80211_reg_rule *rule2;
5 kx
5 kx struct ieee80211_reg_rule tmp_optimized_rule;
5 kx struct ieee80211_reg_rule *tmp_opt_rule;
5 kx
5 kx struct ieee80211_reg_rule *target_rule;
5 kx
5 kx unsigned int optimized = 0;
5 kx int r;
5 kx
5 kx if (rule_idx > rd->n_reg_rules)
5 kx return 0;
5 kx
5 kx rule1 = &rd->reg_rules[rule_idx];
5 kx
5 kx memset(&tmp_optimized_rule, 0, sizeof(struct ieee80211_reg_rule));
5 kx tmp_opt_rule = &tmp_optimized_rule;
5 kx
5 kx memset(opt_rule, 0, sizeof(*opt_rule));
5 kx
5 kx for (i = 0; i < rd->n_reg_rules; i++) {
5 kx if (rule_idx == i)
5 kx continue;
5 kx rule2 = &rd->reg_rules[i];
5 kx if (opt_map[rule_idx].key != opt_map[i].key)
5 kx continue;
5 kx
5 kx target_rule = optimized ? opt_rule : rule1;
5 kx r = reg_rule_optimize(target_rule, rule2, tmp_opt_rule);
5 kx if (r != 0)
5 kx continue;
5 kx memcpy(opt_rule, tmp_opt_rule, sizeof(*tmp_opt_rule));
5 kx
5 kx if (!opt_map[i].optimized) {
5 kx opt_map[i].optimized = true;
5 kx optimized++;
5 kx }
5 kx if (!opt_map[rule_idx].optimized) {
5 kx opt_map[rule_idx].optimized = true;
5 kx optimized++;
5 kx }
5 kx }
5 kx return optimized;
5 kx }
5 kx
5 kx struct ieee80211_regdomain *
5 kx reglib_optimize_regdom(struct ieee80211_regdomain *rd)
5 kx {
5 kx struct ieee80211_regdomain *opt_rd = NULL;
5 kx struct ieee80211_reg_rule *reg_rule;
5 kx struct ieee80211_reg_rule *reg_rule_dst;
5 kx struct ieee80211_reg_rule optimized_reg_rule;
5 kx struct ieee80211_reg_rule *opt_reg_rule;
5 kx struct reglib_optimize_map *opt_map;
5 kx unsigned int i, idx = 0, non_opt = 0, opt = 0;
5 kx size_t num_rules, size_of_regd, size_of_opt_map;
5 kx unsigned int num_opts = 0;
5 kx
5 kx size_of_opt_map = (rd->n_reg_rules + 2) *
5 kx sizeof(struct reglib_optimize_map);
5 kx opt_map = malloc(size_of_opt_map);
5 kx if (!opt_map)
5 kx return NULL;
5 kx
5 kx memset(opt_map, 0, size_of_opt_map);
5 kx memset(&optimized_reg_rule, 0, sizeof(struct ieee80211_reg_rule));
5 kx
5 kx opt_reg_rule = &optimized_reg_rule;
5 kx
5 kx for (i = 0; i < rd->n_reg_rules; i++) {
5 kx reg_rule = &rd->reg_rules[i];
5 kx opt_map[i].key = reglib_rule_key(reg_rule);
5 kx }
5 kx for (i = 0; i < rd->n_reg_rules; i++) {
5 kx reg_rule = &rd->reg_rules[i];
5 kx if (opt_map[i].optimized)
5 kx continue;
5 kx num_opts = reg_rule_optimize_rd(rd, i, opt_reg_rule, opt_map);
5 kx if (!num_opts)
5 kx non_opt++;
5 kx else
5 kx opt += (num_opts ? 1 : 0);
5 kx }
5 kx
5 kx num_rules = non_opt + opt;
5 kx
5 kx if (num_rules > rd->n_reg_rules)
5 kx goto fail_opt_map;
5 kx
5 kx size_of_regd = reglib_array_len(sizeof(struct ieee80211_regdomain),
5 kx num_rules + 1,
5 kx sizeof(struct ieee80211_reg_rule));
5 kx
5 kx opt_rd = malloc(size_of_regd);
5 kx if (!opt_rd)
5 kx goto fail_opt_map;
5 kx memset(opt_rd, 0, size_of_regd);
5 kx
5 kx opt_rd->n_reg_rules = num_rules;
5 kx opt_rd->alpha2[0] = rd->alpha2[0];
5 kx opt_rd->alpha2[1] = rd->alpha2[1];
5 kx opt_rd->dfs_region = rd->dfs_region;
5 kx
5 kx memset(opt_map, 0, size_of_opt_map);
5 kx memset(&optimized_reg_rule, 0, sizeof(struct ieee80211_reg_rule));
5 kx
5 kx opt_reg_rule = &optimized_reg_rule;
5 kx
5 kx for (i = 0; i < rd->n_reg_rules; i++) {
5 kx reg_rule = &rd->reg_rules[i];
5 kx opt_map[i].key = reglib_rule_key(reg_rule);
5 kx }
5 kx
5 kx for (i = 0; i < rd->n_reg_rules; i++) {
5 kx reg_rule = &rd->reg_rules[i];
5 kx reg_rule_dst = &opt_rd->reg_rules[idx];
5 kx if (opt_map[i].optimized)
5 kx continue;
5 kx num_opts = reg_rule_optimize_rd(rd, i, opt_reg_rule, opt_map);
5 kx if (!num_opts)
5 kx memcpy(reg_rule_dst, reg_rule, sizeof(struct ieee80211_reg_rule));
5 kx else
5 kx memcpy(reg_rule_dst, opt_reg_rule, sizeof(struct ieee80211_reg_rule));
5 kx idx++;
5 kx }
5 kx
5 kx if (idx != num_rules)
5 kx goto fail;
5 kx
5 kx for (i = 0; i < opt_rd->n_reg_rules; i++) {
5 kx reg_rule = &opt_rd->reg_rules[i];
5 kx if (!is_valid_reg_rule(reg_rule))
5 kx goto fail;
5 kx }
5 kx
5 kx free(opt_map);
5 kx return opt_rd;
5 kx fail:
5 kx free(opt_rd);
5 kx fail_opt_map:
5 kx free(opt_map);
5 kx return NULL;
5 kx }