[PATCH v4 3/4] hostapd: ap: add AFC client support
Lorenzo Bianconi
lorenzo at kernel.org
Tue Apr 16 08:46:48 PDT 2024
[...]
> > +
> > +#define HOSTAPD_AFC_RETRY_TIMEOUT 180
> > +#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */
> > +#define HOSTAPD_AFC_BUFSIZE 4096
> With the afc-reply.json, the response is 4842 bytes, so, this needs to
> be increased.
ack, I will fix it.
> > +
> > +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx);
> > +
> > +
> > +static struct json_object *
> > +hostapd_afc_build_location_request(struct hostapd_iface *iface)
> > +{
> > + struct json_object *location_obj, *center_obj, *ellipse_obj;
> > + struct json_object *elevation_obj, *str_obj;
> > + struct hostapd_config *iconf = iface->conf;
> > + bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
> > +
> > + location_obj = json_object_new_object();
> > + if (!location_obj)
> > + return NULL;
> > +
> > + if (iconf->afc.location.type != LINEAR_POLYGON) {
> > + struct afc_linear_polygon *lp =
> > + &iconf->afc.location.linear_polygon_data[0];
> > +
> > + ellipse_obj = json_object_new_object();
> > + if (!ellipse_obj)
> > + goto error;
> > +
> > + center_obj = json_object_new_object();
> > + if (!center_obj)
> > + goto error;
> > +
> > + json_object_object_add(ellipse_obj, "center", center_obj);
> > +
> > + str_obj = json_object_new_double(lp->longitude);
> If the config file doesn't have the entries, then these pointers NULL
> and hostapd
> crashes.
ack, I will fix it.
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(center_obj, "longitude", str_obj);
> > + str_obj = json_object_new_double(lp->latitude);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(center_obj, "latitude", str_obj);
> > + }
> > +
> > + switch (iconf->afc.location.type) {
> > + case LINEAR_POLYGON: {
> > + struct json_object *outer_boundary_obj;
> > + int i;
> > +
> > + outer_boundary_obj = json_object_new_object();
> > + if (!outer_boundary_obj)
> > + goto error;
> > +
> > + json_object_object_add(location_obj, "linearPolygon",
> > + outer_boundary_obj);
> > + ellipse_obj = json_object_new_array();
> > + if (!ellipse_obj)
> > + goto error;
> > +
> > + json_object_object_add(outer_boundary_obj, "outerBoundary",
> > + ellipse_obj);
> > + for (i = 0;
> > + i < iconf->afc.location.n_linear_polygon_data; i++) {
> > + struct afc_linear_polygon *lp =
> > + &iconf->afc.location.linear_polygon_data[i];
> > + center_obj = json_object_new_object();
> > + if (!center_obj)
> > + goto error;
> > +
> > + json_object_array_add(ellipse_obj, center_obj);
> > + str_obj = json_object_new_double(lp->longitude);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(center_obj, "longitude",
> > + str_obj);
> > + str_obj = json_object_new_double(lp->latitude);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(center_obj, "latitude",
> > + str_obj);
> > + }
> > + break;
> > + }
> > + case RADIAL_POLYGON: {
> > + struct json_object *outer_boundary_obj;
> > + int i;
> > +
> > + json_object_object_add(location_obj, "radialPolygon",
> > + ellipse_obj);
> > +
> > + outer_boundary_obj = json_object_new_array();
> > + if (!outer_boundary_obj)
> > + goto error;
> > +
> > + json_object_object_add(ellipse_obj, "outerBoundary",
> > + outer_boundary_obj);
> > + for (i = 0;
> > + i < iconf->afc.location.n_radial_polygon_data; i++) {
> > + struct afc_radial_polygon *rp =
> > + &iconf->afc.location.radial_polygon_data[i];
> > + struct json_object *angle_obj;
> > +
> > + angle_obj = json_object_new_object();
> > + if (!angle_obj)
> > + goto error;
> > +
> > + json_object_array_add(outer_boundary_obj, angle_obj);
> > +
> > + str_obj = json_object_new_double(rp->angle);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(angle_obj, "angle", str_obj);
> > + str_obj = json_object_new_double(rp->length);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(angle_obj, "length", str_obj);
> > + }
> > + break;
> > + }
> > + case ELLIPSE:
> > + default:
> > + json_object_object_add(location_obj, "ellipse", ellipse_obj);
> > +
> > + str_obj = json_object_new_int(iconf->afc.location.major_axis);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(ellipse_obj, "majorAxis", str_obj);
> > + str_obj = json_object_new_int(iconf->afc.location.minor_axis);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(ellipse_obj, "minorAxis", str_obj);
> > + str_obj = json_object_new_int(iconf->afc.location.orientation);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(ellipse_obj, "orientation", str_obj);
> > + break;
> > + }
> > +
> > + elevation_obj = json_object_new_object();
> > + if (!elevation_obj)
> > + goto error;
> > +
> > + json_object_object_add(location_obj, "elevation",
> > + elevation_obj);
> > + str_obj = json_object_new_double(iconf->afc.location.height);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(elevation_obj, "height", str_obj);
> > + str_obj = json_object_new_string(iconf->afc.location.height_type);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(elevation_obj, "heightType", str_obj);
> > + str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(elevation_obj, "verticalUncertainty",
> > + str_obj);
> > + str_obj = json_object_new_int(is_ap_indoor);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(location_obj, "indoorDeployment", str_obj);
> > +
> > + return location_obj;
> > +
> > +error:
> > + json_object_put(location_obj);
> > + return NULL;
> > +}
> > +
> > +
> > +static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class)
> > +{
> > + struct json_object *chan_list_obj, *str_obj;
> > + const struct oper_class_map *oper_class;
> > + int chan_offset, chan;
> > +
> > + oper_class = get_oper_class(NULL, op_class);
> > + if (!oper_class)
> > + return NULL;
> > +
> > + chan_list_obj = json_object_new_array();
> > + if (!chan_list_obj)
> > + return NULL;
> > +
> > + switch (op_class) {
> > + case 132: /* 40MHz */
> > + chan_offset = 2;
> > + break;
> > + case 133: /* 80MHz */
> > + chan_offset = 6;
> > + break;
> > + case 134: /* 160MHz */
> > + chan_offset = 14;
> > + break;
> > + default:
> > + chan_offset = 0;
> > + break;
> > + }
> > +
> > + for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
> > + chan += oper_class->inc) {
> > + str_obj = json_object_new_int(chan + chan_offset);
> > + if (!str_obj) {
> > + json_object_put(chan_list_obj);
> > + return NULL;
> > + }
> > + json_object_array_add(chan_list_obj, str_obj);
> > + }
> > +
> > + return chan_list_obj;
> > +}
> > +
> > +
> > +static struct json_object *
> > +hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
> > +{
> > + struct json_object *op_class_list_obj, *str_obj;
> > + struct hostapd_config *iconf = iface->conf;
> > + int i;
> > +
> > + op_class_list_obj = json_object_new_array();
> > + if (!op_class_list_obj)
> > + return NULL;
> > +
> > + for (i = 0; i < iconf->afc.n_op_class; i++) {
> > + struct json_object *op_class_obj, *chan_list_obj;
> > + u8 op_class = iconf->afc.op_class[i];
> > +
> > + if (!is_6ghz_op_class(op_class))
> > + continue;
> > +
> > + op_class_obj = json_object_new_object();
> > + if (!op_class_obj)
> > + goto error;
> > +
> > + json_object_array_add(op_class_list_obj, op_class_obj);
> > + str_obj = json_object_new_int(op_class);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(op_class_obj, "globalOperatingClass",
> > + str_obj);
> > +
> > + chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
> > + if (!chan_list_obj)
> > + goto error;
> > +
> > + json_object_object_add(op_class_obj, "channelCfi",
> > + chan_list_obj);
> > + }
> > +
> > + return op_class_list_obj;
> > +
> > +error:
> > + json_object_put(op_class_list_obj);
> > + return NULL;
> > +}
> > +
> > +
> > +static struct json_object *
> > +hostapd_afc_build_request(struct hostapd_iface *iface)
> > +{
> > + struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj;
> > + struct json_object *s2_obj, *str_obj, *location_obj;
> > + struct hostapd_config *iconf = iface->conf;
> > + struct json_object *op_class_list_obj;
> > + int i;
> > +
> > + l1_obj = json_object_new_object();
> > + if (!l1_obj)
> > + return NULL;
> > +
> > + if (iconf->afc.request.version) {
> > + str_obj = json_object_new_string(iconf->afc.request.version);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(l1_obj, "version", str_obj);
> > + }
> > +
> > + la1_obj = json_object_new_array();
> > + if (!la1_obj)
> > + goto error;
> > +
> > + json_object_object_add(l1_obj, "availableSpectrumInquiryRequests",
> > + la1_obj);
> > + l2_obj = json_object_new_object();
> > + if (!l2_obj)
> > + goto error;
> > +
> > + json_object_array_add(la1_obj, l2_obj);
> > + if (iconf->afc.request.id) {
> > + str_obj = json_object_new_string(iconf->afc.request.id);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(l2_obj, "requestId", str_obj);
> > + }
> > +
> > + s2_obj = json_object_new_object();
> > + if (!s2_obj)
> > + goto error;
> > +
> > + json_object_object_add(l2_obj, "deviceDescriptor", s2_obj);
> > + if (iconf->afc.request.sn) {
> > + str_obj = json_object_new_string(iconf->afc.request.sn);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(s2_obj, "serialNumber", str_obj);
> > + }
> > +
> > + la2_obj = json_object_new_array();
> > + if (!la2_obj)
> > + goto error;
> > +
> > + json_object_object_add(s2_obj, "certificationId", la2_obj);
> > + for (i = 0; i < iconf->afc.n_cert_ids; i++) {
> > + struct json_object *obj;
> > +
> > + obj = json_object_new_object();
> > + if (!obj)
> > + goto error;
> > +
> > + json_object_array_add(la2_obj, obj);
> > + str_obj =
> > + json_object_new_string(iconf->afc.cert_ids[i].rulset);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(obj, "rulesetId", str_obj);
> > + str_obj = json_object_new_string(iconf->afc.cert_ids[i].id);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(obj, "id", str_obj);
> > + }
> > +
> > + location_obj = hostapd_afc_build_location_request(iface);
> > + if (!location_obj)
> > + goto error;
> > +
> > + json_object_object_add(l2_obj, "location", location_obj);
> > + str_obj = json_object_new_int(iconf->afc.min_power);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(l2_obj, "minDesiredPower", str_obj);
> > +
> > + if (iconf->afc.n_freq_range) {
> > + struct json_object *freq_obj;
> > +
> > + freq_obj = json_object_new_array();
> > + if (!freq_obj)
> > + goto error;
> > +
> > + json_object_object_add(l2_obj, "inquiredFrequencyRange",
> > + freq_obj);
> > + for (i = 0; i < iconf->afc.n_freq_range; i++) {
> > + struct afc_freq_range *fr = &iconf->afc.freq_range[i];
> > + struct json_object *obj;
> > +
> > + obj = json_object_new_object();
> > + if (!obj)
> > + goto error;
> > +
> > + json_object_array_add(freq_obj, obj);
> > + str_obj = json_object_new_int(fr->low_freq);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(obj, "lowFrequency", str_obj);
> > + str_obj = json_object_new_int(fr->high_freq);
> > + if (!str_obj)
> > + goto error;
> > +
> > + json_object_object_add(obj, "highFrequency", str_obj);
> > + }
> > + }
> > +
> > + op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
> > + if (!op_class_list_obj)
> > + goto error;
> > +
> > + json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj);
> > +
> > + wpa_printf(MSG_DEBUG, "Pending AFC request: %s",
> > + json_object_get_string(l1_obj));
> > +
> > + return l1_obj;
> > +
> > +error:
> > + json_object_put(l1_obj);
> > +
> > + return NULL;
> > +}
> > +
> > +
> > +static int
> > +hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
> > + struct json_object *reply_elem_obj)
> > +{
> > + struct afc_freq_range_elem *f = NULL;
> > + struct json_object *obj;
> > + int i, count = 0;
> > +
> > + if (!json_object_object_get_ex(reply_elem_obj,
> > + "availableFrequencyInfo", &obj))
> > + return 0;
> > +
> > + for (i = 0; i < json_object_array_length(obj); i++) {
> > + struct json_object *range_elem_obj, *freq_range_obj;
> > + struct json_object *high_freq_obj, *low_freq_obj;
> > + struct json_object *max_psd_obj;
> > +
> > + range_elem_obj = json_object_array_get_idx(obj, i);
> > + if (!range_elem_obj)
> > + continue;
> > +
> > + if (!json_object_object_get_ex(range_elem_obj,
> > + "frequencyRange",
> > + &freq_range_obj))
> > + continue;
> > +
> > + if (!json_object_object_get_ex(freq_range_obj,
> > + "lowFrequency",
> > + &low_freq_obj))
> > + continue;
> > +
> > + if (!json_object_object_get_ex(freq_range_obj,
> > + "highFrequency",
> > + &high_freq_obj))
> > + continue;
> > +
> > + if (!json_object_object_get_ex(range_elem_obj, "maxPsd",
> > + &max_psd_obj) &&
> > + !json_object_object_get_ex(range_elem_obj, "maxPSD",
> > + &max_psd_obj))
> > + continue;
> > +
> > + f = os_realloc_array(f, count + 1, sizeof(*f));
> > + if (!f)
> > + return -ENOMEM;
> > +
> > + f[count].low_freq = json_object_get_int(low_freq_obj);
> > + f[count].high_freq = json_object_get_int(high_freq_obj);
> > + f[count++].max_psd = json_object_get_int(max_psd_obj);
> > + }
> > + iface->afc.freq_range = f;
> > + iface->afc.num_freq_range = count;
> > +
> > + return 0;
> > +}
> > +
> > +
> > +static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list,
> > + int *chan_list_size, u8 op_class,
> > + int center_chan, int power)
> > +{
> > + int num_low_subchan, ch, count = *chan_list_size;
> > + struct afc_chan_info_elem *c = *chan_list;
> > +
> > + switch (op_class) {
> > + case 132: /* 40MHz */
> > + num_low_subchan = 2;
> > + break;
> > + case 133: /* 80MHz */
> > + num_low_subchan = 6;
> > + break;
> > + case 134: /* 160MHz */
> > + num_low_subchan = 14;
> > + break;
> > + default:
> > + num_low_subchan = 0;
> > + break;
> > + }
> > +
> > + for (ch = center_chan - num_low_subchan;
> > + ch <= center_chan + num_low_subchan; ch += 4) {
> > + int i;
> > +
> > + for (i = 0; i < count; i++) {
> > + if (c[i].chan == ch)
> > + break;
> > + }
> > +
> > + if (i == count) {
> > + c = os_realloc_array(c, count + 1, sizeof(*c));
> > + if (!c)
> > + return -ENOMEM;
> > +
> > + c[count].chan = ch;
> > + c[count++].power = power;
> > + }
> > + }
> > +
> > + *chan_list_size = count;
> > + *chan_list = c;
> > +
> > + return 0;
> > +}
> > +
> > +
> > +static int
> > +hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
> > + struct json_object *reply_elem_obj)
> > +{
> > + struct afc_chan_info_elem *c = NULL;
> > + struct json_object *obj;
> > + int i, count = 0;
> > +
> > + if (!json_object_object_get_ex(reply_elem_obj,
> > + "availableChannelInfo", &obj))
> > + return 0;
> > +
> > + for (i = 0; i < json_object_array_length(obj); i++) {
> > + struct json_object *range_elem_obj, *op_class_obj;
> > + struct json_object *chan_cfi_obj, *max_eirp_obj;
> > + int ch, op_class;
> > +
> > + range_elem_obj = json_object_array_get_idx(obj, i);
> > + if (!range_elem_obj)
> > + continue;
> > +
> > + if (!json_object_object_get_ex(range_elem_obj,
> > + "globalOperatingClass",
> > + &op_class_obj))
> > + continue;
> > +
> > + if (!json_object_object_get_ex(range_elem_obj, "maxEirp",
> > + &max_eirp_obj))
> > + continue;
> > +
> > + if (!json_object_object_get_ex(range_elem_obj, "channelCfi",
> > + &chan_cfi_obj))
> > + continue;
> > +
> > + op_class = json_object_get_int(op_class_obj);
> > + for (ch = 0;
> > + ch < json_object_array_length(chan_cfi_obj); ch++) {
> > + struct json_object *pwr_obj;
> > + struct json_object *ch_obj;
> > + int channel, power;
> > +
> > + ch_obj = json_object_array_get_idx(chan_cfi_obj, ch);
> > + if (!ch_obj)
> > + continue;
> > +
> > + pwr_obj = json_object_array_get_idx(max_eirp_obj, ch);
> > + if (!pwr_obj)
> > + continue;
> > +
> > + channel = json_object_get_int(ch_obj);
> > + power = json_object_get_int(pwr_obj);
> > +
> > + hostad_afc_update_chan_info(&c, &count, op_class,
> > + channel, power);
> > + }
> > + iface->afc.chan_info_list = c;
> > + iface->afc.num_chan_info = count;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +
> > +static int hostad_afc_get_timeout(struct json_object *obj)
> > +{
> > + time_t t, now;
> > + struct tm tm;
> > +
> > + if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ",
> > + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
> > + &tm.tm_min, &tm.tm_sec) <= 0)
> > + return HOSTAPD_AFC_TIMEOUT;
> > +
> > + tm.tm_year -= 1900;
> > + tm.tm_mon -= 1;
> > + tm.tm_isdst = -1;
> > + t = mktime(&tm);
> > + time(&now);
> > +
> > + return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100;
> > +}
> > +
> > +
> > +static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
> > +{
> > + struct json_object *payload_obj, *reply_obj, *version_obj;
> > + struct hostapd_config *iconf = iface->conf;
> > + int i, request_timeout = -1, ret = -EINVAL;
> > +
> > + wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply);
> > + payload_obj = json_tokener_parse(reply);
> > + if (!payload_obj)
> > + return -EINVAL;
> This function could use some error logs, esp. as its related to parsing.
ack, I will fix it.
> > +
> > + if (!json_object_object_get_ex(payload_obj, "version", &version_obj))
> > + return -EINVAL;
> > +
> > + if (iconf->afc.request.version &&
> > + os_strcmp(iconf->afc.request.version,
> > + json_object_get_string(version_obj)))
> > + return -EINVAL;
> > +
> > + if (!json_object_object_get_ex(payload_obj,
> > + "availableSpectrumInquiryResponses",
> > + &reply_obj))
> > + return -EINVAL;
> > +
> > + for (i = 0; i < json_object_array_length(reply_obj); i++) {
> > + struct json_object *reply_elem_obj, *obj, *status_obj;
> > + int j, status = -EINVAL;
> > +
> > + reply_elem_obj = json_object_array_get_idx(reply_obj, i);
> > + if (!reply_elem_obj)
> > + continue;
> > +
> > + if (!json_object_object_get_ex(reply_elem_obj, "requestId",
> > + &obj))
> > + continue;
> > +
> > + if (iconf->afc.request.id &&
> > + os_strcmp(iconf->afc.request.id,
> > + json_object_get_string(obj)))
> > + continue;
> > +
> > + if (!json_object_object_get_ex(reply_elem_obj, "rulesetId",
> > + &obj))
> > + continue;
> > +
> > + for (j = 0; j < iconf->afc.n_cert_ids; j++) {
> > + if (!os_strcmp(iconf->afc.cert_ids[j].rulset,
> > + json_object_get_string(obj)))
> > + break;
> > + }
> > +
> > + if (j == iconf->afc.n_cert_ids)
> > + continue;
> > +
> > + if (!json_object_object_get_ex(reply_elem_obj, "response",
> > + &obj))
> > + continue;
> > +
> > + if (json_object_object_get_ex(obj, "shortDescription",
> > + &status_obj))
> > + wpa_printf(MSG_DEBUG, "AFC reply element %d: %s",
> > + i, json_object_get_string(status_obj));
> > +
> > + if (json_object_object_get_ex(obj, "responseCode",
> > + &status_obj))
> > + status = json_object_get_int(status_obj);
> > +
> > + if (status < 0)
> > + continue;
> > +
> > + if (hostad_afc_parse_available_freq_info(iface,
> > + reply_elem_obj) ||
> > + hostad_afc_parse_available_chan_info(iface,
> > + reply_elem_obj))
> > + continue;
> > +
> > + if (json_object_object_get_ex(reply_elem_obj,
> > + "availabilityExpireTime",
> > + &obj)) {
> > + int timeout = hostad_afc_get_timeout(obj);
> > +
> > + if (request_timeout < 0 || timeout < request_timeout)
> > + request_timeout = timeout;
> > + }
> > +
> > + ret = status;
> > + }
> > +
> > + iface->afc.data_valid = true;
> > + iface->afc.timeout = request_timeout;
> > + if (iface->afc.timeout < 0)
> > + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
> > +
> > + return ret;
> > +}
> > +
> > +
> > +static int hostapd_afc_send_receive(struct hostapd_iface *iface)
> > +{
> > + struct hostapd_config *iconf = iface->conf;
> > + json_object *request_obj = NULL;
> > + struct timeval sock_timeout = {
> > + .tv_sec = 5,
> > + };
> > + struct sockaddr_un addr = {
> > + .sun_family = AF_UNIX,
> > +#ifdef __FreeBSD__
> > + .sun_len = sizeof(addr),
> > +#endif /* __FreeBSD__ */
> > + };
> > + char buf[HOSTAPD_AFC_BUFSIZE] = {};
> > + const char *request;
> > + int sockfd, ret;
> > + fd_set read_set;
> > +
> > + if (iface->afc.data_valid) {
> > + /* AFC data already downloaded from the server */
> > + return 0;
> > + }
> > +
> > + if (!iconf->afc.socket) {
> > + wpa_printf(MSG_ERROR, "Missing AFC socket string");
> > + return -EINVAL;
> > + }
> > +
> > + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
> > + if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
> > + wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
> > + iconf->afc.socket);
> > + return -EINVAL;
> > + }
> > +
> > + os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path));
> > + sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
> > + if (sockfd < 0) {
> > + wpa_printf(MSG_ERROR, "Failed creating AFC socket");
> > + return sockfd;
> > + }
> > +
> > + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> > + wpa_printf(MSG_ERROR, "Failed connecting AFC socket");
> > + ret = -EIO;
> > + goto close_sock;
> > + }
> > +
> > + request_obj = hostapd_afc_build_request(iface);
> > + if (!request_obj) {
> > + ret = -ENOMEM;
> > + goto close_sock;
> > + }
> > +
> > + request = json_object_to_json_string(request_obj);
> > + if (send(sockfd, request, strlen(request), 0) < 0) {
> > + wpa_printf(MSG_ERROR, "Failed sending AFC request");
> > + ret = -EIO;
> > + goto close_sock;
> > + }
> > +
> > + FD_ZERO(&read_set);
> > + FD_SET(sockfd, &read_set);
> > + if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) {
> > + wpa_printf(MSG_ERROR, "Select failed on AFC socket");
> > + ret = -errno;
> > + goto close_sock;
> > + }
> > +
> > + if (!FD_ISSET(sockfd, &read_set)) {
> > + ret = -EIO;
> > + goto close_sock;
> > + }
> > +
> > + ret = recv(sockfd, buf, sizeof(buf) - 1, 0);
> > + if (ret <= 0)
> > + goto close_sock;
> > +
> > + ret = hostapd_afc_parse_reply(iface, buf);
> > +close_sock:
> > + json_object_put(request_obj);
> > + close(sockfd);
> > +
> > + return ret;
> > +}
> > +
> > +
> > +static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
> > +{
> > + const struct oper_class_map *oper_class;
> > + int ch;
> > +
> > + oper_class = get_oper_class(NULL, iface->conf->op_class);
> > + if (!oper_class)
> > + return false;
> > +
> > + for (ch = oper_class->min_chan; ch <= oper_class->max_chan;
> > + ch += oper_class->inc) {
> > + struct hostapd_hw_modes *mode = iface->current_mode;
> > + int i;
> > +
> > + for (i = 0; i < mode->num_channels; i++) {
> > + struct hostapd_channel_data *chan = &mode->channels[i];
> > +
> > + if (chan->chan == ch &&
> > + !(chan->flag & HOSTAPD_CHAN_DISABLED))
> > + return true;
> > + }
> > + }
> > +
> > + return false;
> > +}
> > +
> > +
> > +int hostapd_afc_handle_request(struct hostapd_iface *iface)
> > +{
> > + struct hostapd_config *iconf = iface->conf;
> > + int ret;
> > +
> > + /* AFC is required just for standard power AP */
> > + if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
> > + return 1;
> > +
> > + if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq))
> > + return 1;
> > +
> > + if (iface->state == HAPD_IFACE_ACS)
> > + return 1;
> > +
> > + ret = hostapd_afc_send_receive(iface);
> > + if (ret < 0) {
> > + /*
> > + * If the connection to the AFCD failed, resched for a
> > + * future attempt.
> > + */
> > + wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
> > + if (ret == -EIO)
> > + ret = 0;
> > + goto resched;
> > + }
> > +
> > + hostap_afc_disable_channels(iface);
> > + if (!hostapd_afc_has_usable_chans(iface))
> > + goto resched;
> > +
> > + /* Trigger an ACS freq scan */
> > + iconf->channel = 0;
> > + iface->freq = 0;
> > +
> > + if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
> > + wpa_printf(MSG_ERROR, "Could not start ACS");
> > + ret = -EINVAL;
> > + }
> > +
> > +resched:
> > + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
> > + eloop_register_timeout(iface->afc.timeout, 0,
> > + hostapd_afc_timeout_handler, iface, NULL);
> > +
> > + return ret;
> > +}
> > +
> > +
> > +static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
> > +{
> > + os_free(iface->afc.chan_info_list);
> > + os_free(iface->afc.freq_range);
> > +
> > + iface->afc.num_freq_range = 0;
> > + iface->afc.num_chan_info = 0;
> > +
> > + iface->afc.chan_info_list = NULL;
> > + iface->afc.freq_range = NULL;
> > +
> > + iface->afc.data_valid = false;
> > +}
> > +
> > +
> > +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
> > +{
> > + struct hostapd_iface *iface = eloop_ctx;
> > + bool restart_iface = true;
> > +
> > + hostapd_afc_delete_data_from_server(iface);
> > + if (iface->state != HAPD_IFACE_ENABLED) {
> > + /* Hostapd is not fully enabled yet, toggle the interface */
> > + goto restart_interface;
> > + }
> > +
> > + if (hostapd_afc_send_receive(iface) < 0 ||
> > + hostapd_get_hw_features(iface)) {
> > + restart_iface = false;
> > + goto restart_interface;
> > + }
> > +
> > + if (hostapd_is_usable_chans(iface))
> > + goto resched;
> > +
> > + restart_iface = hostapd_afc_has_usable_chans(iface);
> > + if (restart_iface) {
> > + /* Trigger an ACS freq scan */
> > + iface->conf->channel = 0;
> > + iface->freq = 0;
> > + }
> > +
> > +restart_interface:
> > + hostapd_disable_iface(iface);
> > + if (restart_iface)
> > + hostapd_enable_iface(iface);
> > +resched:
> > + eloop_register_timeout(iface->afc.timeout, 0,
> > + hostapd_afc_timeout_handler, iface, NULL);
> > +}
> > +
> > +
> > +void hostapd_afc_stop(struct hostapd_iface *iface)
> > +{
> > + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
> > +}
> > +
> > +
> > +void hostap_afc_disable_channels(struct hostapd_iface *iface)
> > +{
> > + struct hostapd_hw_modes *mode;
> > + int i;
> > +
> > + if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A)
> > + return;
> > +
> > + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
> > + return;
> > +
> > + if (!iface->afc.data_valid)
> > + return;
> > +
> > + mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A];
> > + for (i = 0; i < mode->num_channels; i++) {
> > + struct hostapd_channel_data *chan = &mode->channels[i];
> > + int j;
> > +
> > + if (!is_6ghz_freq(chan->freq))
> > + continue;
> > +
> > + for (j = 0; j < iface->afc.num_freq_range; j++) {
> > + if (chan->freq >= iface->afc.freq_range[j].low_freq &&
> > + chan->freq <= iface->afc.freq_range[j].high_freq)
> > + break;
> > + }
> > +
> > + if (j != iface->afc.num_freq_range)
> > + continue;
> > +
> > + for (j = 0; j < iface->afc.num_chan_info; j++) {
> > + if (chan->chan == iface->afc.chan_info_list[j].chan)
> > + break;
> > + }
> > +
> > + if (j != iface->afc.num_chan_info)
> > + continue;
> > +
> > + chan->flag |= HOSTAPD_CHAN_DISABLED;
> Please add a debug print with channel info.
ack, I will fix it.
Regards,
Lorenzo
> > + }
> > +}
> > diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
> > index 1a18df617..ca67aeb41 100644
> > --- a/src/ap/ap_config.c
> > +++ b/src/ap/ap_config.c
> > @@ -1035,6 +1035,22 @@ void hostapd_config_free(struct hostapd_config *conf)
> > #endif /* CONFIG_ACS */
> > wpabuf_free(conf->lci);
> > wpabuf_free(conf->civic);
> > +#ifdef CONFIG_AFC
> > + os_free(conf->afc.socket);
> > + os_free(conf->afc.request.version);
> > + os_free(conf->afc.request.id);
> > + os_free(conf->afc.request.sn);
> > + for (i = 0; i < conf->afc.n_cert_ids; i++) {
> > + os_free(conf->afc.cert_ids[i].rulset);
> > + os_free(conf->afc.cert_ids[i].id);
> > + }
> > + os_free(conf->afc.cert_ids);
> > + os_free(conf->afc.location.height_type);
> > + os_free(conf->afc.location.linear_polygon_data);
> > + os_free(conf->afc.location.radial_polygon_data);
> > + os_free(conf->afc.freq_range);
> > + os_free(conf->afc.op_class);
> > +#endif /* CONFIG_AFC */
> >
> > os_free(conf);
> > }
> > diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
> > index 754d55331..2330163c4 100644
> > --- a/src/ap/ap_config.h
> > +++ b/src/ap/ap_config.h
> > @@ -1225,6 +1225,53 @@ struct hostapd_config {
> > MBSSID_ENABLED = 1,
> > ENHANCED_MBSSID_ENABLED = 2,
> > } mbssid;
> > +
> > +#ifdef CONFIG_AFC
> > + struct {
> > + char *socket;
> > + struct {
> > + char *version;
> > + char *id;
> > + char *sn;
> > + } request;
> > + unsigned int n_cert_ids;
> > + struct cert_id {
> > + char *rulset;
> > + char *id;
> > + } *cert_ids;
> > + struct {
> > + enum afc_location_type {
> > + ELLIPSE,
> > + LINEAR_POLYGON,
> > + RADIAL_POLYGON,
> > + } type;
> > + unsigned int n_linear_polygon_data;
> > + struct afc_linear_polygon {
> > + double longitude;
> > + double latitude;
> > + } *linear_polygon_data;
> > + unsigned int n_radial_polygon_data;
> > + struct afc_radial_polygon {
> > + double length;
> > + double angle;
> > + } *radial_polygon_data;
> > + int major_axis;
> > + int minor_axis;
> > + int orientation;
> > + double height;
> > + char *height_type;
> > + int vertical_tolerance;
> > + } location;
> > + unsigned int n_freq_range;
> > + struct afc_freq_range {
> > + int low_freq;
> > + int high_freq;
> > + } *freq_range;
> > + unsigned int n_op_class;
> > + unsigned int *op_class;
> > + int min_power;
> > + } afc;
> > +#endif /* CONFIG_AFC */
> > };
> >
> >
> > diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
> > index f8cb6432d..940a2a0af 100644
> > --- a/src/ap/hostapd.c
> > +++ b/src/ap/hostapd.c
> > @@ -714,6 +714,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
> > static void hostapd_cleanup_iface(struct hostapd_iface *iface)
> > {
> > wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
> > + hostapd_afc_stop(iface);
> > eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
> > eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
> > NULL);
> > @@ -2454,6 +2455,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
> > }
> > #endif /* CONFIG_MESH */
> >
> > +#ifdef CONFIG_IEEE80211AX
> > + /* check AFC for 6GHz channels. */
> > + res = hostapd_afc_handle_request(iface);
> > + if (res <= 0) {
> > + if (res < 0)
> > + goto fail;
> > + return res;
> > + }
> > +#endif /* CONFIG_IEEE80211AX */
> > +
> > if (!delay_apply_cfg &&
> > hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
> > hapd->iconf->channel,
> > @@ -2852,6 +2863,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
> >
> > hostapd_set_state(iface, HAPD_IFACE_DISABLED);
> >
> > + hostapd_afc_stop(iface);
> > eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
> > iface->wait_channel_update = 0;
> > iface->is_no_ir = false;
> > @@ -2925,6 +2937,10 @@ void hostapd_interface_free(struct hostapd_iface *iface)
> > __func__, iface->bss[j]);
> > os_free(iface->bss[j]);
> > }
> > +#ifdef CONFIG_AFC
> > + os_free(iface->afc.chan_info_list);
> > + os_free(iface->afc.freq_range);
> > +#endif
> > hostapd_cleanup_iface(iface);
> > }
> >
> > diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
> > index affe4f604..b74dc75f6 100644
> > --- a/src/ap/hostapd.h
> > +++ b/src/ap/hostapd.h
> > @@ -700,9 +700,54 @@ struct hostapd_iface {
> >
> > /* Configured freq of interface is NO_IR */
> > bool is_no_ir;
> > +
> > +#ifdef CONFIG_AFC
> > + struct {
> > + int timeout;
> > + unsigned int num_freq_range;
> > + struct afc_freq_range_elem {
> > + int low_freq;
> > + int high_freq;
> > + /**
> > + * max eirp power spectral density received from
> > + * the AFC coordinator for this band
> > + */
> > + int max_psd;
> > + } *freq_range;
> > + unsigned int num_chan_info;
> > + struct afc_chan_info_elem {
> > + int chan;
> > + /**
> > + * max eirp power received from the AFC coordinator
> > + * for this channel
> > + */
> > + int power;
> > + } *chan_info_list;
> > + bool data_valid;
> > + } afc;
> > +#endif /* CONFIG_AFC */
> > };
> >
> > /* hostapd.c */
> > +#ifdef CONFIG_AFC
> > +int hostapd_afc_handle_request(struct hostapd_iface *iface);
> > +void hostapd_afc_stop(struct hostapd_iface *iface);
> > +void hostap_afc_disable_channels(struct hostapd_iface *iface);
> > +#else
> > +static inline int hostapd_afc_handle_request(struct hostapd_iface *iface)
> > +{
> > + return 1;
> > +}
> > +
> > +static inline void hostapd_afc_stop(struct hostapd_iface *iface)
> > +{
> > +}
> > +
> > +static inline void hostap_afc_disable_channels(struct hostapd_iface *iface)
> > +{
> > +}
> > +#endif /* CONFIG_AFC */
> > +
> > int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
> > int (*cb)(struct hostapd_iface *iface,
> > void *ctx), void *ctx);
> > diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
> > index e652d7504..222f3dc05 100644
> > --- a/src/ap/hw_features.c
> > +++ b/src/ap/hw_features.c
> > @@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
> > iface->hw_features = modes;
> > iface->num_hw_features = num_modes;
> >
> > + hostap_afc_disable_channels(iface);
> > +
> > for (i = 0; i < num_modes; i++) {
> > struct hostapd_hw_modes *feature = &modes[i];
> > int dfs_enabled = hapd->iconf->ieee80211h &&
> > --
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 228 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/hostap/attachments/20240416/eb5c65ca/attachment-0001.sig>
More information about the Hostap
mailing list