cherry-pick dhcp option43 handling

This commit is contained in:
suvendhu 2023-01-12 11:46:38 +05:30
parent 44571036d1
commit 91e473e780
3 changed files with 142 additions and 143 deletions

View file

@ -543,6 +543,26 @@ This script then writes the gateway identity in `/var/state/cwmp` if CWMP is ena
Datamodel (libbbf) API's reads the gateway information from `/var/state/cwmp` to populate Device.GatewayInfo. object. If gateway information is not available in `/var/state/cwmp` then it reads the 'db' (Same source used for Device.DeviceInfo. object) and maps that data to populate Device.GatewayInfo. object.
## Provisioning of CWMP Agent through DHCP Server
If CPE receives DHCP option 43 in DHCP offer from upstream network it exposes the same in its UBUS method (e.g. ifstatus wan).
```bash
root@iopsys-44d43771b000:~# ifstatus wan
{
...
...
"data": {
"vendorspecinf": "XXXXXXXXXXXXXXXXXXX"
}
}
```
CWMP Agent uses DHCP option 43 for provisioning if CWMP and DHCP discovery is enabled in the device. ICWMPD package adds a [DHCP client hook script](files/etc/udhcpc.user.d/udhcpc_icwmp_opt43.user), which parses DHCP option 43 value to extract informations like ACS URL, minimum interval for session retry, retry interval multiplier for session retry and provisioning code that specifies the primary service provider and other provisioning information, which may be used by the ACS to determine service provider-specific customization and provisioning parameters.
This script then writes the provisioning information in `/etc/config/cwmp`.
CWMP Agent reads the provisioning information from `/etc/config/cwmp` before starting a session.
> Note: If in any time ACS configures `Device.ManagementServer.URL` then DHCP discovery is disabled automatically.
## Dependencies
To successfully build icwmp, the following libraries are needed:

143
config.c
View file

@ -23,144 +23,6 @@
pthread_mutex_t mutex_config_load = PTHREAD_MUTEX_INITIALIZER;
void get_dhcp_vend_info_cb(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
{
if (req == NULL || msg == NULL)
return;
char **v_info = (char **)req->priv;
if (v_info == NULL)
return;
struct blob_attr *param;
size_t rem;
enum {
E_VENDOR_INFO,
__E_MAX
};
const struct blobmsg_policy p[__E_MAX] = {
{ "vendorspecinf", BLOBMSG_TYPE_STRING },
};
blobmsg_for_each_attr(param, msg, rem) {
if (strcmp(blobmsg_name(param), "data") == 0) {
struct blob_attr *tb[__E_MAX] = {NULL};
if (blobmsg_parse(p, __E_MAX, tb, blobmsg_data(param), blobmsg_len(param)) != 0) {
return;
}
if (tb[E_VENDOR_INFO]) {
char *info = blobmsg_get_string(tb[E_VENDOR_INFO]);
if (info == NULL)
info = "";
int len = strlen(info) + 1;
*v_info = (char *)malloc(len);
if (*v_info == NULL)
return;
memset(*v_info, 0, len);
snprintf(*v_info, len, "%s", info);
}
break;
}
}
return;
}
bool configure_dhcp_options(char *vendspecinf)
{
if (vendspecinf == NULL) {
CWMP_LOG(DEBUG, "No vendor specific info found");
return false;
}
// extract url from vendor info
int len = CWMP_STRLEN(vendspecinf) + 1;
char vend_info[len];
memset(vend_info, 0, len);
snprintf(vend_info, len, "%s", vendspecinf);
if (strncmp(vend_info, "http://", 7) == 0 || strncmp(vend_info, "https://", 8) == 0) {
uci_set_value_by_path(UCI_DHCP_ACS_URL, vend_info, UCI_STANDARD_CONFIG);
CWMP_LOG(DEBUG, "dhcp url: %s", vend_info);
cwmp_commit_package("cwmp", UCI_STANDARD_CONFIG);
return true;
}
bool update_uci = false;
char *temp = strtok(vend_info, " ");
while (temp) {
if (strncmp(temp, "1=", 2) == 0) {
char *pos = temp + 2;
if (CWMP_STRLEN(pos)) {
uci_set_value_by_path(UCI_DHCP_ACS_URL, pos, UCI_STANDARD_CONFIG);
CWMP_LOG(DEBUG, "dhcp url: %s", pos);
update_uci = true;
}
}
if (strncmp(temp, "2=", 2) == 0) {
char *pos = temp + 2;
if (CWMP_STRLEN(pos)) {
uci_set_value_by_path(UCI_DHCP_CPE_PROV_CODE, pos, UCI_STANDARD_CONFIG);
update_uci = true;
}
}
if (strncmp(temp, "3=", 2) == 0) {
char *pos = temp + 2;
if (CWMP_STRLEN(pos)) {
uci_set_value_by_path(UCI_DHCP_ACS_RETRY_MIN_WAIT_INTERVAL, pos, UCI_STANDARD_CONFIG);
update_uci = true;
}
}
if (strncmp(temp, "4=", 2) == 0) {
char *pos = temp + 2;
if (CWMP_STRLEN(pos)) {
uci_set_value_by_path(UCI_DHCP_ACS_RETRY_INTERVAL_MULTIPLIER, pos, UCI_STANDARD_CONFIG);
update_uci = true;
}
}
temp = strtok(NULL, " ");
}
if (update_uci)
cwmp_commit_package("cwmp", UCI_STANDARD_CONFIG);
cwmp_uci_reinit();
return update_uci;
}
static void get_dhcp_vendor_info(char *intf)
{
if (intf == NULL)
return;
char ubus_obj[100] = {0};
snprintf(ubus_obj, sizeof(ubus_obj), "network.interface.%s", intf);
struct blob_buf b;
memset(&b, 0, sizeof(struct blob_buf));
blob_buf_init(&b, 0);
char *vendor_info = NULL;
if (icwmp_ubus_invoke(ubus_obj, "status", b.head, get_dhcp_vend_info_cb, &vendor_info) == 0) {
CWMP_LOG(DEBUG, "vendor info: %s", vendor_info);
configure_dhcp_options(vendor_info);
}
FREE(vendor_info);
blob_buf_free(&b);
}
static char* get_value_from_uci_option(struct uci_option *tb) {
if (tb == NULL)
return NULL;
@ -333,13 +195,8 @@ int get_global_config(struct config *conf)
bool discovery_enable = false;
error = uci_get_value(UCI_DHCP_DISCOVERY_PATH, &value);
// now read the vendor info from ifstatus before reading the DHCP_ACS_URL from uci
if (error == CWMP_OK && value != NULL) {
discovery_enable = uci_str_to_bool(value);
if (discovery_enable == true && conf->default_wan_iface != NULL) {
get_dhcp_vendor_info(conf->default_wan_iface);
}
}
FREE(value);

View file

@ -0,0 +1,122 @@
#!/bin/sh
. /lib/functions.sh
DHCP_ACS_URL=""
DHCP_PROV_CODE=""
MIN_WAIT_INVL=""
INVL_MULTIPLIER=""
get_opt43() {
# Check if option value is in encapsulated form
local opt43="$1"
local len="$2"
[ "$len" -gt "2" ] || return
first_byte=${opt43:0:2}
first_byte=$(printf "%d\n" "0x$first_byte")
if [ $len -ge 4 ] && [ $first_byte -ge 1 ] && [ $first_byte -le 4 ]; then
# it is in encapsulated form
# opt43 encapsulated vendor-specific option has data in below format
# Code Len Data item Code Len Data item Code
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
# | T1 | n | d1 | d2 | ... | T2 | n | D1 | D2 | ... | ... |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
#hex-string 2 character=1 Byte
# length in hex string will be twice of actual Byte length
data="${opt43}"
rem_len="${len}"
# parsing of suboption of option 43
while [ $rem_len -gt 0 ]; do
# get the suboption id
sub_opt_id=${data:0:2}
sub_opt_id=$(printf "%d\n" "0x$sub_opt_id")
# get the length of suboption
sub_opt_len=${data:2:2}
sub_opt_len=$(printf "%d\n" "0x$sub_opt_len")
sub_opt_len=$(( sub_opt_len * 2 ))
# get the value of sub option starting 4 means starting after length
sub_opt_val=${data:4:${sub_opt_len}}
# assign the value found in sub option
case "${sub_opt_id}" in
"1") DHCP_ACS_URL=$(echo -n $sub_opt_val | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf && echo '')
;;
"2") DHCP_PROV_CODE=$(echo -n $sub_opt_val | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf && echo '')
;;
"3") MIN_WAIT_INVL=$(echo -n $sub_opt_val | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf && echo '')
;;
"4") INVL_MULTIPLIER=$(echo -n $sub_opt_val | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf && echo '')
;;
esac
# add 2 bytes for sub_opt id and sub_opt len field
sub_opt_end=$(( sub_opt_len + 4 ))
# fetch next sub option hex string
data=${data:${sub_opt_end}:${len}}
# update the remaining sub option hex string length
rem_len=$((rem_len - sub_opt_end))
done
else
DHCP_ACS_URL=$(echo -n $opt43 | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf && echo '')
fi
}
config_load cwmp
config_get_bool enable_cwmp cpe enable 1
config_get wan_intf cpe default_wan_interface "wan"
config_get dhcp_discovery acs dhcp_discovery "0"
discovery_enable=0
if [ "$dhcp_discovery" = "1" ] || [ "$dhcp_discovery" = "true" ] || [ "$dhcp_discovery" = "enable" ]; then
discovery_enable=1
fi
if [ "$enable_cwmp" = "0" ] || [ "$discovery_enable" = "0" ]; then
return 0
fi
if [ "${wan_intf}" == "${INTERFACE}" ]; then
if [ -n "$opt43" ]; then
len=$(printf "$opt43"|wc -c)
get_opt43 "$opt43" "$len"
fi
if [ -z "$DHCP_ACS_URL" ]; then
return 0
fi
sec=$(uci -q get cwmp.acs)
if [ -z "${sec}" ]; then
return 0
fi
uci -q set cwmp.acs.dhcp_url="$DHCP_ACS_URL"
if [ -n "$MIN_WAIT_INVL" ]; then
uci -q set cwmp.acs.dhcp_retry_min_wait_interval="$MIN_WAIT_INVL"
fi
if [ -n "$INVL_MULTIPLIER" ]; then
uci -q set cwmp.acs.dhcp_retry_interval_multiplier="$INVL_MULTIPLIER"
fi
uci -q commit cwmp
sec=$(uci -q get cwmp.cpe)
if [ -z "${sec}" ]; then
return 0
fi
if [ -n "$DHCP_PROV_CODE" ]; then
uci -q set cwmp.cpe.dhcp_provisioning_code="$DHCP_PROV_CODE"
uci -q commit cwmp
fi
fi