I do not want to overload you with the issues or changes, so just an ideas for improving your software:
When I started the tests with Automated RT Tester against the p-net stack, none of the test passed.
So I went through them, and for now just only the DCP tests I have resolved somehow. But probably my code changes goes against your program philosophy, maybe it would be better to notice you where are the (some of the) problems and the possible solutions:
- Chaning and storing the IP addres and station name (mainly in the pf_cmina.c file):
- The tester changes the IP address and/or the station name of the device in two forms - permanently and temporarily. The variable temp in the
pf_cmina_dcp_set_ind()
function shows it. When the change is permanent, the os_set_ip_suite()
function is called, which is OK. But when the change is temporary, it means (according to documentation) that the IP suite or name must be changed, BUT also the default value of 0.0.0.0 and/or empty name must be stored, so after the device restart, the device responds with 0.0.0.0 etc. In your stack the IP suite and name change is postponed by setting the flag net->cmina_commit_ip_suite
. But it works (sometimes) for the permanent change only. There is also another problem when the tester changes the values permanently and immediately after that temporarily, the stack is confused what is changing.
- I have solved it by the way that I call the
os_set_ip_suite()
(with the extra temp flag) function immediately in the case PF_DCP_SUB_IP_PAR:
and the other cases
(lines 316, 334, 360), but it is probably far from your program philosophy to do it that way. The postponed call of os_set_ip_suite() in the pf_cmina_dcp_set_commit()
function I have commented out.
- There is another issue in the same function
pf_cmina_dcp_set_ind()
. The values of IP suite and/or station name must be checked for validity. So some check in the case PF_DCP_SUB_IP_PAR:
(line 316), case PF_DCP_SUB_IP_SUITE:
(line 334), case PF_DCP_SUB_DEV_PROP_NAME:
(line 360) are needed. The stack should response with an error for invalid IP address, netmask, gateway or name. I have implemented some simple functions for that, maybe it would help you:
/**
* @internal
* Check the validity of IP address and subnet mask
* In case of invalid values log to Error.
* @param p_ip_suite In: pointer to IP suite structure to be checked
* @return false if the values are incorrect
* true if IP address and subnet mask are valid
*/
static bool pf_cmina_ip_suite_is_valid(
const pf_ip_suite_t *p_ip_suite)
{
bool ret = true;
const uint32_t ip_addr = p_ip_suite->ip_addr;
const uint32_t ip_mask = p_ip_suite->ip_mask;
const uint32_t ip_gateway = p_ip_suite->ip_gateway;
const uint32_t network_class = (ip_addr >> 24);
const uint32_t masked_addr = ip_addr & ~(ip_mask);
if (network_class < 128) /* network class A */
{
if (masked_addr >= (256 * 256 * 256))
{
ret = false;
}
}
else if (network_class < 192) /* network class B */
{
if (masked_addr >= (256 * 256))
{
ret = false;
}
}
else if (network_class < 224) /* network class C */
{
if (masked_addr >= (256))
{
ret = false;
}
}
else /* network class D or E */
{
ret = false;
}
/* check the gateway - must be in the same subnet */
if (ip_gateway != 0)
{
const uint32_t inv_masked_addr = ip_addr & ip_mask;
const uint32_t inv_masked_gw = ip_gateway & ip_mask;
if (inv_masked_addr != inv_masked_gw)
{
ret = false;
}
}
if (ret == false) /* display error */
{
LOG_WARNING(PF_DCP_LOG, "CMINA(%d): invalid IP suite:\n\taddr %d.%d.%d.%d mask %d.%d.%d.%d gw %d.%d.%d.%d\n",
__LINE__,
(ip_addr >> 24) & 0xFF,
(ip_addr >> 16) & 0xFF,
(ip_addr >> 8) & 0xFF,
(ip_addr) & 0xFF,
(ip_mask >> 24) & 0xFF,
(ip_mask >> 16) & 0xFF,
(ip_mask >> 8) & 0xFF,
(ip_mask) & 0xFF,
(ip_gateway >> 24) & 0xFF,
(ip_gateway >> 16) & 0xFF,
(ip_gateway >> 8) & 0xFF,
(ip_gateway) & 0xFF);
}
return ret;
}
/**
* @internal
* Check the validity of station name.
* Only several checks are done now.
* Valid name is formed by:
- 1 or more labels, separated by [.]
- Total length is 1 to 240
- Label length is 1 to 63
- Labels consist of [a-z0-9-]
- Labels do not start with [-]
- Labels do not end with [-]
- Labels do not use multiple concatenated [-] except for IETF RFC 5890
- The first label does not have the form "port-xyz" or "port-xyz-abcde" with a, b, c, d, e,
x, y, z = 0...9, to avoid wrong similarity with the field AliasNameValue
- Station-names do not have the form a.b.c.d with a, b, c, d = 0...999
* @param net In: The p-net stack instance
* @param p_name In: Station name to be checked
* @param name_length In: name length
* @return false if the name is bad formed
* true if the name is valid
*/
static bool pf_cmina_name_of_station_is_valid(
const pnet_t *net,
const char *p_name,
uint16_t name_length)
{
if (name_length >= sizeof(net->cmina_temp_dcp_ase.name_of_station))
{
return false;
}
if (p_name[0] == '-')
{
return false;
}
/* check the port-xyz label */
if(name_length >= 6)
{
if (strncmp(p_name, "port-", 5) == 0)
{
if (isdigit(p_name[5]))
{
return false;
}
}
}
/* go through labels */
uint32_t n_numeric_labels = 0;
uint32_t n_consecutive_numeric_labels = 0;
uint16_t src_pos = 0;
char last_c = 0;
while (src_pos < name_length)
{
bool b_is_digit_only = true;
char prev_c = 0;
while (src_pos < name_length)
{
const char c = p_name[src_pos];
last_c = c;
src_pos++;
if (isdigit(c))
{
; /* digit is valid character, do nothing */
}
else if (islower(c) || c == '-')
{
b_is_digit_only = false;
}
else if (c == '.') /* end of label */
{
if (prev_c == 0) /* only dot in label? */
{
b_is_digit_only = false;
}
break;
}
else /* invalid character */
{
return false;
}
prev_c = c;
}
if (b_is_digit_only)
{
n_consecutive_numeric_labels++;
n_numeric_labels++;
}
else
{
n_consecutive_numeric_labels = 0;
}
}
/* check the last character here to better utilize the cache */
if (last_c == '-')
{
return false;
}
/* check the invalid label a.b.c.d where abcd are digits only */
if (n_consecutive_numeric_labels == 4 &&
n_numeric_labels == 4 &&
isdigit(last_c)) /* the last character must be a digit */
{
return false;
}
return true;
}
These functions are not optimal nor complete, but maybe they will be helpfull.
- The DCP Identify (5) Request All (Block Option All Selector 255) response is probably bad formed. The Automated RT Tester does not expect the MAC address block (I don't know why). It is an error for it. But when the tester asks for MAC address, it must be given. So I have commented out the device_options[] entry in file pf_dcp.c, line 104
// {PF_DCP_OPT_IP, PF_DCP_SUB_IP_MAC},
and also the skip flag on the line 306
case PF_DCP_SUB_IP_MAC:
// skip = true;
break;
which solved the problem (The Request All does not iterate through the device_options[], but the particular request for MAC address will not be skipped).
- At the end of the
pf_dcp_get_req()
function, when there is an error, the response probably should be instead of yours
ret = pf_dcp_put_block(p_dst, p_dst_pos, dst_max, opt, sub, true, 0, sizeof(block_error), &block_error);
something like
ret = pf_dcp_put_block(p_dst,
p_dst_pos,
dst_max,
PF_DCP_OPT_CONTROL,
PF_DCP_SUB_CONTROL_RESPONSE,
true,
((uint16_t)(opt) << 8) | ((uint16_t)(sub)),
sizeof(block_error),
&block_error);
It means the error control block should be composed od ...CONTROL and ...CONTROL_RESPONSE values, with the option and suboption set inside it.
A lot of text, I know, so other issues I will leave for the next time :-)