diff --git a/src/EtherSia.h b/src/EtherSia.h index 0bc4f5d..dfe82f1 100644 --- a/src/EtherSia.h +++ b/src/EtherSia.h @@ -14,6 +14,7 @@ #include "MACAddress.h" #include "IPv6Address.h" #include "IPv6Packet.h" +#include "IPv6Prefix.h" #include "Socket.h" #include "UDPSocket.h" @@ -90,6 +91,28 @@ class EtherSia { _autoConfigurationEnabled = true; } + /** + * Restricts autoconfiguration to a specified prefix, + * which Router Advertisements must be contained by. + * + * @note autoconfig restriction is disabled by default + * @param prefix the prefix to restrict to + */ + inline void enablePrefixRestriction(IPv6Prefix *prefix) { + memcpy(_whitelistedPrefix, prefix, sizeof(IPv6Prefix)); + _prefixRestrictionEnabled = true; + } + + /** + * Disables autoconfiguration restriction. + * + * @note autoconfig restriction is disabled by default + */ + inline void disablePrefixRestriction() { + memset(_whitelistedPrefix, 0, sizeof(IPv6Prefix)); + _prefixRestrictionEnabled = false; + } + /** * Manually set the global IPv6 address for the Ethernet Interface * from an IPv6Address object @@ -373,6 +396,7 @@ class EtherSia { IPv6Address _linkLocalAddress; /**< The IPv6 Link-local address of the Ethernet Interface */ IPv6Address _globalAddress; /**< The IPv6 Global address of the Ethernet Interface */ IPv6Address _dnsServerAddress; /**< The IPv6 address of the configured DNS server */ + IPv6Prefix _whitelistedPrefix; /**< The whitelisted IPv6 Prefix */ /** The MAC address of this Ethernet controller */ MACAddress _localMac; @@ -392,6 +416,9 @@ class EtherSia { /** Flag indicating if the buffer contains a valid packet we received */ boolean _autoConfigurationEnabled; + /** Flag indicating if a whitelisted Prefix is enabled */ + boolean _prefixRestrictionEnabled; + /** * Checks the Ethernet Layer 2 addresses * @return true if packet should be accepted diff --git a/src/IPv6Prefix.cpp b/src/IPv6Prefix.cpp new file mode 100644 index 0000000..12ad1f6 --- /dev/null +++ b/src/IPv6Prefix.cpp @@ -0,0 +1,162 @@ +#include +#include "MACAddress.h" +#include "IPv6Address.h" +#include "IPv6Prefix.h" +#include "util.h" + +IPv6Prefix::IPv6Prefix() +{ + setZero(); +} + +IPv6Prefix::IPv6Prefix(const IPv6Address *address, const uint8_t length) { + memcpy(_address, address, sizeof(IPv6Address)); + _length = length; +} + +IPv6Prefix::IPv6Prefix(const char *prefixstr) +{ + fromString(prefixstr); +} + +IPv6Prefix::IPv6Prefix(const __FlashStringHelper *prefixstr) +{ + fromString(prefixstr); +} + +boolean IPv6Prefix::fromString(const char *prefixstr) +{ + setZero(); + + bool size_reached = false; + int8_t address_chars_read = 0; + int8_t prefix_chars_read = 0; + uint8_t prefix_chars[3]; + char address[45]; + for (uint8_t i = 0; i < strlen(prefixstr); i++) { + if (address_chars_read > 45) { + // Aboort if more than 45 Addres characters are read + return false; + } else if (!size_reached && prefixstr[i] == '/') { + // Begin to read prefix if seperator is read + size_reached = true; + // Zero address memory and write the read address to it + memset(address, 0, 45); + memcpy((void *) address, prefixstr, address_chars_read); + // Abort if address is invalid + if (_address.fromString(address) != true) return false; + } else if (!size_reached) { + // Increment address character counter + address_chars_read++; + } else if (size_reached) { + if(prefixstr[i] >= 48 && prefixstr[i] <= 57 && prefix_chars_read < 3) { + // Read prefix size chars + prefix_chars[prefix_chars_read] = prefixstr[i] - 48; + prefix_chars_read++; + } else { + // Abort on invalid characters and more than 4 prefix characters + return false; + } + } + } + uint8_t multiplier = 1; + uint8_t length = 0; + for (int8_t i = prefix_chars_read; i > 0; i--) { + // Calculate Prefix size from characters + length += prefix_chars[i - 1] * multiplier; + multiplier = multiplier * 10; + } + return setLength(length); +} + +boolean IPv6Prefix::fromString(const __FlashStringHelper *prefixstr) +{ + char ramStr[44]; + // Copy the string from flash program memory into RAM + strcpy_P(ramStr, (const char*)prefixstr); + return fromString(ramStr); +} + +const IPv6Address* IPv6Prefix::address() const +{ + return &_address; +} + +uint8_t IPv6Prefix::getLength() const +{ + return _length; +} + +boolean IPv6Prefix::setLength(uint8_t prefix_length) +{ + // Abort and return false if size is greater than 128 + if(prefix_length > 128) return false; + // save it otherwise + _length = prefix_length; + + // Bits to mask (intial value == prefix_length) + uint8_t to_mask = this->getLength(); + for(uint8_t i=0; i<16; i++) { + uint8_t mask = 0xFF; + if(to_mask < 8 ) { + uint8_t bit_remove = 8 - to_mask; + for(uint8_t j = 0; j < bit_remove; j++) { + mask = mask - (1 << j); + } + to_mask = 0; + } else { + to_mask = to_mask - 8; + } + // mask address + this->_address[i] = this->_address[i] & mask; + } + + return true; +} + +boolean IPv6Prefix::contains(const IPv6Prefix *prefix) +{ + if(prefix->getLength() < this->getLength()) { + // If candidate prefix is smaller than current prefix, return false + return false; + } + + IPv6Prefix candidate; + memcpy(candidate, prefix, sizeof(IPv6Prefix)); + candidate.setLength(this->getLength()); + return (candidate == *(this)); +} + +IPv6Prefix::operator uint8_t*() +{ + return _address; +} + +void IPv6Prefix::setZero() +{ + _address.setZero(); + _length = 0; +} + +boolean IPv6Prefix::operator==(const IPv6Prefix& prefix) const +{ + return *(address()) == *(prefix.address()) && getLength() == prefix.getLength(); +} + +boolean IPv6Prefix::operator!=(const IPv6Prefix& address) const +{ + return !(*this == address); +} + +void IPv6Prefix::print(Print &p) const +{ + _address.print(p); + p.print("/"); + p.print(this->_length); +} + +void IPv6Prefix::println(Print &p) const +{ + this->print(p); + p.println(); +} diff --git a/src/IPv6Prefix.h b/src/IPv6Prefix.h new file mode 100644 index 0000000..75dff0f --- /dev/null +++ b/src/IPv6Prefix.h @@ -0,0 +1,126 @@ +/** + * Header file for the IPv6Prefix class + * @file IPv6Prefix.h + */ + +#ifndef IPv6Prefix_H +#define IPv6Prefix_H + +#include +#include "IPv6Address.h" + +/** + * Class for the storage and manipulation of IPv6 prefixes. + */ +class IPv6Prefix { +private: + IPv6Address _address; + uint8_t _length; + +public: + /** + * Constructor for a new / all-zero IPv6 /0 prefix + */ + IPv6Prefix(); + + /** + * Constructor from a IPv6 address and prefix length + * @param address the prefixes address + * @param length the prefixes length + */ + IPv6Prefix(const IPv6Address *address, const uint8_t length); + + /** + * Constructor from a human readable IPv6 prefix string + * @param prefixstr the prefix to parse + */ + IPv6Prefix(const char *prefixstr); + + /** + * Constructor from a human readable IPv6 prefix Flash string (use the F() macro) + * @param prefixstr the prefix to parse + */ + IPv6Prefix(const __FlashStringHelper *prefixstr); + + /** + * Set prefix to the a human readable IPv6 string + * The prefix string can contain double colon :: notation to save space + * @param prefixstr an IPv6 prefix as a null-terminated string + * @return true if the string was parsed successfully + */ + boolean fromString(const char *prefixstr); + + /** + * Set prefix to the a human readable IPv6 Flash string (use the F() macro) + * The prefix string can contain double colon :: notation to save space + * @param prefixstr an IPv6 prefix as a null-terminated string + * @return true if the string was parsed successfully + */ + boolean fromString(const __FlashStringHelper *prefixstr); + + /** + * Returns the prefix address + * @return the prefix address + */ + const IPv6Address* address() const; + + /** + * Returns the prefix length + * @return the prefix length + */ + uint8_t getLength() const ; + + /** + * Sets the prefix length + * @param prefix_length the new prefix length + * @return returns true if new length is valid, false if otherwise + */ + boolean setLength(uint8_t prefix_length); + + /** + * Checks if another IPv6 Prefix is contained by the prefix + * @param prefix the prefix to be checked + * @return true if prefix is contained, false if not + */ + boolean contains(const IPv6Prefix *prefix); + + /** + * Cast the IPv6 prefix to an array of octets. + */ + operator uint8_t*(); + + /** + * Set the prefix to all-zeros (::/0) + */ + void setZero(); + + /** + * Check if the prefix equals another IPv6 prefix. + * @param prefix the second prefix to compare to + * @return true if the two prefixes are the same + */ + boolean operator==(const IPv6Prefix& prefix) const; + + /** + * Check if the prefix is not equal to another IPv6 prefix. + * @param prefix the second prefix to compare to + * @return true if the two prefixes are the same + */ + boolean operator!=(const IPv6Prefix& prefix) const; + + /** + * Print a IPv6 prefix to a stream as a human readable string. + * @param print The stream to print to (defaults to Serial) + */ + void print(Print &print=Serial) const; + + /** + * Print a IPv6 prefix to a stream with line ending. + * @param print The stream to print to (defaults to Serial) + */ + void println(Print &print=Serial) const; + +} __attribute__((__packed__)); + + #endif + \ No newline at end of file diff --git a/src/icmp6.cpp b/src/icmp6.cpp index 7dd046a..bc15864 100644 --- a/src/icmp6.cpp +++ b/src/icmp6.cpp @@ -179,6 +179,13 @@ void EtherSia::icmp6ProcessRA() _routerMac = *((MACAddress*)&ptr[2]); break; case ICMP6_OPTION_PREFIX_INFORMATION: + if(_prefixRestrictionEnabled == true) { + struct icmp6_prefix_information *icmp_p = (struct icmp6_prefix_information*)&ptr[2]; + IPv6Prefix advertised_prefix(&icmp_p->prefix, icmp_p->prefix_length); + if(!_whitelistedPrefix.contains(&advertised_prefix)) { + return; + } + } icmp6ProcessPrefix( (struct icmp6_prefix_information*)&ptr[2] ); diff --git a/tests/35_check_ipv6_prefix.tc b/tests/35_check_ipv6_prefix.tc new file mode 100644 index 0000000..d2377f8 --- /dev/null +++ b/tests/35_check_ipv6_prefix.tc @@ -0,0 +1,96 @@ +#include "Arduino.h" + +#include "MACAddress.h" +#include "IPv6Prefix.h" +#suite IPv6Prefix + +const uint8_t zero[17] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 +}; + + +#test new_IPv6Prefix +IPv6Prefix prefix; +ck_assert_mem_eq(zero, prefix, 17); + + +#test setZero +uint8_t expect[17] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x0F +}; +IPv6Prefix *prefix = (IPv6Prefix *)expect; +prefix->setZero(); +ck_assert_mem_eq(zero, prefix, 17); + +#test constructorFromAddressLength +uint8_t expect[17] = { + 0x20, 0x01, 0x06, 0x7c, 0x2e, 0xd8, 0x61, 0x00, + 0xf6, 0xf2, 0x6d, 0xff, 0xfe, 0x3e, 0x9c, 0xac, + 0x40 +}; +IPv6Address address = IPv6Address("2001:67c:2ed8:6100:f6f2:6dff:fe3e:9cac"); +uint8_t length = 64; +IPv6Prefix prefix = IPv6Prefix(&address, length); +ck_assert_mem_eq(expect, prefix, 17); + +#test constructorFromString +uint8_t expect[17] = { + 0x20, 0x01, 0x06, 0x7c, 0x2e, 0xd8, 0x61, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40 +}; +IPv6Prefix prefix = IPv6Prefix("2001:67c:2ed8:6100:f6f2:6dff:fe3e:9cac/64"); +ck_assert_mem_eq(expect, prefix, 17); + +#test fromString +uint8_t expect[17] = { + 0x20, 0x01, 0x06, 0x7c, 0x2e, 0xd8, 0x61, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40 +}; +IPv6Prefix prefix; +prefix.fromString("2001:67c:2ed8:6100:f6f2:6dff:fe3e:9cac/64"); +ck_assert_mem_eq(expect, prefix, 17); + +#test returnAddress +uint8_t expect[16] = { + 0x20, 0x01, 0x06, 0x7c, 0x2e, 0xd8, 0x61, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +IPv6Prefix prefix; +prefix.fromString("2001:67c:2ed8:6100:f6f2:6dff:fe3e:9cac/64"); +ck_assert_mem_eq(expect, prefix.address(), 16); + +#test getLength +IPv6Prefix prefix; +prefix.fromString("2001:67c:2ed8:6100:f6f2:6dff:fe3e:9cac/64"); +ck_assert(prefix.getLength() == 64); + +#test setLength +uint8_t expect[17] = { + 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10 +}; +IPv6Prefix prefix = IPv6Prefix("2001:67c:2ed8:6100:f6f2:6dff:fe3e:9cac/128"); +prefix.setLength(16); +ck_assert_mem_eq(expect, prefix, 17); + +#test contains +IPv6Prefix containing = IPv6Prefix("2001:67c:2ed8:6100::/64"); +const IPv6Prefix contained = IPv6Prefix("2001:67c:2ed8:6100:f6f2:6dff:fe3e:9cac/128"); +ck_assert(containing.contains(&contained)); + +#test containsNotPrefix +IPv6Prefix containing = IPv6Prefix("2001:67c:2ed8:6100::/64"); +const IPv6Prefix contained = IPv6Prefix("2001:67c:2ed8:6101:f6f2:6dff:fe3e:9cac/128"); +ck_assert(!containing.contains(&contained)); + +#test containsNotLength +IPv6Prefix containing = IPv6Prefix("2001:67c:2ed8:6100::/64"); +const IPv6Prefix contained = IPv6Prefix("2001:67c:2ed8::/48"); +ck_assert(!containing.contains(&contained)); \ No newline at end of file