net: ipv6: Fix IPv6 prefix comparision

If the prefix length % 8 is not 0, then the remaining
bit length was calculated incorrectly and the prefixes
were claimed to match even though they might not be the
same.

Adding a test cases for testing this properly.

Coverity-CID: 157591

Change-Id: I9cb5a73d5cc211ec183176400fa5e2dfd209e2da
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2017-01-18 09:38:20 +02:00
commit ba026941a1
2 changed files with 81 additions and 6 deletions

View file

@ -360,27 +360,36 @@ static inline bool net_is_my_ipv6_maddr(struct in6_addr *maddr)
* @param addr2 Second IPv6 address.
* @param length Prefix length (max length is 128).
*
* @return True if addresses are the same, False otherwise.
* @return True if IPv6 prefixes are the same, False otherwise.
*/
static inline bool net_is_ipv6_prefix(const uint8_t *addr1,
const uint8_t *addr2,
uint8_t length)
{
uint8_t bits = 128 - length;
uint8_t bytes = bits / 8;
uint8_t bytes = length / 8;
uint8_t remain = bits % 8;
uint8_t mask;
if (length > 128) {
return false;
}
if (memcmp(addr1, addr2, 16 - bytes)) {
if (memcmp(addr1, addr2, bytes)) {
return false;
}
return ((addr1[16 - bytes] & ((8 - remain) << 8))
==
(addr2[16 - bytes] & ((8 - remain) << 8)));
if (!remain) {
/* No remaining bits, the prefixes are the same as first
* bytes are the same.
*/
return true;
}
/* Create a mask that has remaining most significant bits set */
mask = ((0xff << (8 - remain)) ^ 0xff) << remain;
return (addr1[bytes] & mask) == (addr2[bytes] & mask);
}
/**

View file

@ -273,6 +273,71 @@ static bool test_init(void)
return true;
}
static bool net_test_cmp_prefix(void)
{
bool st;
struct in6_addr prefix1 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
struct in6_addr prefix2 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x2 } } };
st = net_is_ipv6_prefix((uint8_t *)&prefix1, (uint8_t *)&prefix2, 64);
if (!st) {
TC_ERROR("Prefix /64 compare failed\n");
return false;
}
st = net_is_ipv6_prefix((uint8_t *)&prefix1, (uint8_t *)&prefix2, 65);
if (!st) {
TC_ERROR("Prefix /65 compare failed\n");
return false;
}
/* Set one extra bit in the other prefix for testing /65 */
prefix1.s6_addr[8] = 0x80;
st = net_is_ipv6_prefix((uint8_t *)&prefix1, (uint8_t *)&prefix2, 65);
if (st) {
TC_ERROR("Prefix /65 compare should have failed\n");
return false;
}
/* Set two bits in prefix2, it is now /66 */
prefix2.s6_addr[8] = 0xc0;
st = net_is_ipv6_prefix((uint8_t *)&prefix1, (uint8_t *)&prefix2, 65);
if (!st) {
TC_ERROR("Prefix /65 compare failed\n");
return false;
}
/* Set all remaining bits in prefix2, it is now /128 */
memset(&prefix2.s6_addr[8], 0xff, 8);
st = net_is_ipv6_prefix((uint8_t *)&prefix1, (uint8_t *)&prefix2, 65);
if (!st) {
TC_ERROR("Prefix /65 compare failed\n");
return false;
}
/* Comparing /64 should be still ok */
st = net_is_ipv6_prefix((uint8_t *)&prefix1, (uint8_t *)&prefix2, 64);
if (!st) {
TC_ERROR("Prefix /64 compare failed\n");
return false;
}
/* But comparing /66 should should fail */
st = net_is_ipv6_prefix((uint8_t *)&prefix1, (uint8_t *)&prefix2, 66);
if (st) {
TC_ERROR("Prefix /66 compare should have failed\n");
return false;
}
return true;
}
static bool net_test_send_ns_mcast(void)
{
int ret;
@ -614,6 +679,7 @@ static const struct {
bool (*func)(void);
} tests[] = {
{ "test init", test_init },
{ "IPv6 compare prefix", net_test_cmp_prefix },
{ "IPv6 send NS mcast", net_test_send_ns_mcast },
{ "IPv6 neighbor lookup fail", net_test_nbr_lookup_fail },
{ "IPv6 send NS", net_test_send_ns },