mirror of
https://dev.iopsys.eu/bbf/icwmp.git
synced 2026-03-10 19:27:32 +01:00
Optimized dhcp option handling
This commit is contained in:
parent
fbdb63c77b
commit
79284d1f4e
4 changed files with 276 additions and 147 deletions
22
README.md
22
README.md
|
|
@ -501,11 +501,31 @@ root@iopsys-44d43771b000:~# ifstatus wan
|
|||
}
|
||||
}
|
||||
```
|
||||
ICWMPD package adds a DHCP client hook script (`/etc/udhcpc.user.d/udhcpc_icwmp.user`), which parses DHCP option 125 to extract gateway identity based on Enterprise ID `3561`.
|
||||
ICWMPD package adds a [DHCP client hook script](files/etc/udhcpc.user.d/udhcpc_icwmp_opt125.user), which parses DHCP option 125 to extract gateway identity based on Enterprise ID `3561`.
|
||||
This script then writes the gateway identity in `/var/state/cwmp` if CWMP is enabled in the device.
|
||||
|
||||
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:
|
||||
|
|
|
|||
133
files/etc/udhcpc.user.d/udhcpc_icwmp_opt125.user
Normal file
133
files/etc/udhcpc.user.d/udhcpc_icwmp_opt125.user
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#!/bin/sh
|
||||
|
||||
. /lib/functions.sh
|
||||
|
||||
CLASS=""
|
||||
OUI=""
|
||||
SERIAL=""
|
||||
|
||||
get_vivsoi() {
|
||||
# opt125 environment variable has data in below format
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# | enterprise-number1 |
|
||||
# | |
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# | data-len1 | |
|
||||
# +-+-+-+-+-+-+-+-+ option-data1 |
|
||||
# / /
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -----
|
||||
# | enterprise-number2 | ^
|
||||
# | | |
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
||||
# | data-len2 | | optional
|
||||
# +-+-+-+-+-+-+-+-+ option-data2 | |
|
||||
# / / |
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
||||
# ~ ... ~ V
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -----
|
||||
|
||||
# Enterprise Id Len Sub Op SLen Data Sub Op SLen Data Sub Op SLen Data
|
||||
# +-------------+-----+------+------+----+------+-----+----+-----+------+-----+----+
|
||||
# | id | n | 1 | n1 | D1 | 2 | n2 | D2 | ... | 6 | n6 | D6 |
|
||||
# +-------------+-----+------+------+----+------+-----+----+-----+------+-----+----+
|
||||
|
||||
local opt125="$1"
|
||||
local len="$2"
|
||||
local ent_id
|
||||
|
||||
#hex-string 2 character=1 Byte
|
||||
# length in hex string will be twice of actual Byte length
|
||||
[ "$len" -gt "8" ] || return
|
||||
|
||||
data="${opt125}"
|
||||
rem_len="${len}"
|
||||
while [ $rem_len -gt 0 ]; do
|
||||
ent_id=${data:0:8}
|
||||
ent_id=$(printf "%d\n" "0x$ent_id")
|
||||
|
||||
if [ $ent_id -ne 3561 ]; then
|
||||
len_val=${data:8:2}
|
||||
data_len=$(printf "%d\n" "0x$len_val")
|
||||
# add 4 byte for ent_id and 1 byte for len
|
||||
data_len=$(( data_len * 2 + 10 ))
|
||||
# move ahead data to next enterprise id
|
||||
data=${data:"${data_len}":"${rem_len}"}
|
||||
rem_len=$(( rem_len - $data_len ))
|
||||
continue
|
||||
fi
|
||||
|
||||
# read the length of enterprise data
|
||||
len_val=${data:8:2}
|
||||
opt_len=$(printf "%d\n" "0x$len_val")
|
||||
[ $opt_len -eq 0 ] && return
|
||||
|
||||
# populate the option data of enterprise id
|
||||
sub_data_len=$(( opt_len * 2))
|
||||
# starting 10 means ahead of length field
|
||||
sub_data=${data:10:"${sub_data_len}"}
|
||||
|
||||
# parsing of suboption of option 125
|
||||
while [ $sub_data_len -gt 0 ]; do
|
||||
# get the suboption id
|
||||
sub_opt_id=${sub_data:0:2}
|
||||
sub_opt_id=$(printf "%d\n" "0x$sub_opt_id")
|
||||
|
||||
# get the length of suboption
|
||||
sub_opt_len=${sub_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=${sub_data:4:${sub_opt_len}}
|
||||
|
||||
# assign the value found in sub option
|
||||
case "${sub_opt_id}" in
|
||||
"4") OUI=$(echo -n $sub_opt_val | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf && echo '')
|
||||
;;
|
||||
"5") SERIAL=$(echo -n $sub_opt_val | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf && echo '')
|
||||
;;
|
||||
"6") CLASS=$(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
|
||||
sub_data=${sub_data:${sub_opt_end}:${sub_data_len}}
|
||||
|
||||
# update the remaining sub option hex string length
|
||||
sub_data_len=$((sub_data_len - sub_opt_end))
|
||||
done
|
||||
|
||||
break
|
||||
done
|
||||
}
|
||||
|
||||
config_load cwmp
|
||||
config_get_bool enable_cwmp cpe enable 1
|
||||
config_get wan_intf cpe default_wan_interface "wan"
|
||||
|
||||
if [ "$enable_cwmp" = "0" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "${wan_intf}" == "${INTERFACE}" ]; then
|
||||
if [ -n "$opt125" ]; then
|
||||
len=$(printf "$opt125"|wc -c)
|
||||
get_vivsoi "$opt125" "$len"
|
||||
fi
|
||||
|
||||
mkdir -p /var/state
|
||||
touch /var/state/cwmp
|
||||
sec=$(uci -q -c /var/state get cwmp.gatewayinfo)
|
||||
if [ -z "${sec}" ]; then
|
||||
sec=$(uci -q -c /var/state add cwmp gatewayinfo)
|
||||
uci -q -c /var/state rename cwmp."${sec}"="gatewayinfo"
|
||||
fi
|
||||
|
||||
uci -q -c /var/state set cwmp.gatewayinfo.class="$CLASS"
|
||||
uci -q -c /var/state set cwmp.gatewayinfo.oui="$OUI"
|
||||
uci -q -c /var/state set cwmp.gatewayinfo.serial="$SERIAL"
|
||||
uci -q -c /var/state commit cwmp
|
||||
fi
|
||||
122
files/etc/udhcpc.user.d/udhcpc_icwmp_opt43.user
Normal file
122
files/etc/udhcpc.user.d/udhcpc_icwmp_opt43.user
Normal 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
|
||||
146
src/config.c
146
src/config.c
|
|
@ -25,148 +25,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) {
|
||||
CWMP_LOG(WARNING, "config %s: info is null", __FUNCTION__);
|
||||
continue;
|
||||
}
|
||||
int len = strlen(info) + 1;
|
||||
*v_info = (char *)malloc(len);
|
||||
if (*v_info == NULL) {
|
||||
CWMP_LOG(WARNING, "config %s: v_info is null", __FUNCTION__);
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -338,12 +196,8 @@ int get_global_config()
|
|||
|
||||
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 && cwmp_main->conf.default_wan_iface != NULL) {
|
||||
get_dhcp_vendor_info(cwmp_main->conf.default_wan_iface);
|
||||
}
|
||||
}
|
||||
FREE(value);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue