diff --git a/ipv6.h b/ipv6.h index 30d5b83..2bf1f46 100644 --- a/ipv6.h +++ b/ipv6.h @@ -6,10 +6,13 @@ namespace ipv6 { const uint8_t LEN = 128; const uint8_t ICMP_ECHO_REQUEST_TYPE = 128; const uint8_t ICMP_ECHO_REPLY_TYPE = 129; + const uint8_t ICMP_RADV_TYPE = 134; const uint8_t NH_ICMP = 58; const uint8_t NH_UDP = 17; + extern uint8_t HOP_LIMIT; + class IPv6Addr { private: char address[16]; @@ -18,6 +21,7 @@ namespace ipv6 { IPv6Addr(const char* address); }; + extern const IPv6Addr LINK_LOCAL_ADDRESS; extern const IPv6Addr ALL_NODES_ADDRESS; struct ipv6_packet_header { @@ -64,6 +68,33 @@ namespace ipv6 { const char* payload, /* IPv6 payload */ int len /* IPv6 payload length */ ); + + struct ICMPv6_RADV { + uint8_t type; + uint8_t code; + char checksum[2]; + uint8_t cur_hop_limit; + uint8_t mo_flags; + uint16_t router_lifetime; + uint32_t reachable_time; + uint32_t retrans_time; + }; + + struct ICMPv6_RADV_Option { + uint8_t type; + uint8_t len; + }; + + struct ICMPv6_RADV_PrefixInformation { + uint8_t type; + uint8_t len; + uint8_t prefix_length; + uint8_t la_flags; + uint32_t valid_lifetime; + uint32_t preferred_lifetime; + uint32_t reserved2; + char prefix[16]; + }; } #endif diff --git a/ipv6.ino b/ipv6.ino index ad4865f..61c32f2 100644 --- a/ipv6.ino +++ b/ipv6.ino @@ -3,7 +3,11 @@ namespace ipv6 { /******** costants ********/ -const IPv6Addr ALL_NODES_ADDRESS("\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"); /* ff02::1 */ +const IPv6Addr ALL_NODES_ADDRESS("\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"); /* ff02::1 */ +const IPv6Addr LINK_LOCAL_ADDRESS("\xfe\x80\x00\x00\x00\x00\x00\x00\xde\xad\xbe\xef\x12\x34\x56\x78"); /* fe80::dead:beef:1234:5678 */ /* TODO */ + +/******** variables ********/ +uint8_t HOP_LIMIT = 64; /******** buffer ********/ IPv6Packet packetin; @@ -60,7 +64,7 @@ IPv6Addr::IPv6Addr(const char* address) { IPv6Packet::IPv6Packet() { memset(packet, 0, LEN); *(packet) = 0x60; /* IP version 6 */ - *(packet + 7) = 64; /* hop limit / ttl */ + *(packet + 7) = HOP_LIMIT; /* hop limit / ttl */ } IPv6Packet::IPv6Packet(const char* buffer, int len) { @@ -112,8 +116,11 @@ void IPv6Packet::send() { } void IPv6Packet::doAction() { - /* if destination is my address, or destination is "all nodes" address, then... */ - if ((memcmp(&(ALL_NODES_ADDRESS), packet + 24, 16) == 0) || (memcmp(&(coppino::address), packet + 24, 16) == 0)) { + /* if destination is my address (global or link-local), or destination is "all nodes" address, then... */ + if ( + (memcmp(&(ALL_NODES_ADDRESS), packet + 24, 16) == 0) || + (memcmp(&(LINK_LOCAL_ADDRESS), packet + 24, 16) == 0) || + (memcmp(&(coppino::address), packet + 24, 16) == 0)) { int ilen = this->getLen(); /* input payload length */ int olen; /* output payload length */ @@ -147,42 +154,63 @@ void IPv6Packet::doAction() { int handleICMP(char* output_buffer, const char* input_buffer, int len) { uint8_t type = *(input_buffer); - - if (type == ICMP_ECHO_REQUEST_TYPE) { /* if it is ICMPv6 Echo Request, then... */ - /* compute checksum, and assert validity of incoming message */ - char received_checksum[2]; - memcpy(received_checksum, input_buffer + 2, 2); - memcpy(output_buffer, input_buffer, len); - memset(output_buffer + 2, 0, 2); /* zero-checksum */ - uint16_t computed_checksum = compute_checksum( - packetin.getSrcAddress(), - packetin.getDstAddress(), - NH_ICMP, - len, - output_buffer, - len); + /* compute checksum, and assert validity of incoming message */ + char received_checksum[2]; + memcpy(received_checksum, input_buffer + 2, 2); - if (memcmp(&computed_checksum, received_checksum, 2) == 0) { /* if checksum is valid, then... */ - /* Build an echo reply: - * the reply has the same exact format of the request, - * except for Type and Checksum */ - *(output_buffer) = ICMP_ECHO_REPLY_TYPE; - memset(output_buffer + 2, 0, 2); /* zero-checksum */ + memcpy(output_buffer, input_buffer, len); + memset(output_buffer + 2, 0, 2); /* zero-checksum */ + uint16_t computed_checksum = compute_checksum( + packetin.getSrcAddress(), + packetin.getDstAddress(), + NH_ICMP, + len, + output_buffer, + len); - uint16_t checksum = compute_checksum( - packetin.getDstAddress(), - packetin.getSrcAddress(), - NH_ICMP, - len, - output_buffer, - len); - memcpy(output_buffer + 2, &checksum, 2); - - return len; - } + if (memcmp(&computed_checksum, received_checksum, 2) != 0) { /* if checksum is not valid, return */ + return -1; } + if (type == ICMP_ECHO_REQUEST_TYPE) { /* if it is ICMPv6 Echo Request, then... */ + /* Build an echo reply: + * the reply has the same exact format of the request, + * except for Type and Checksum */ + *(output_buffer) = ICMP_ECHO_REPLY_TYPE; + memset(output_buffer + 2, 0, 2); /* zero-checksum */ + + uint16_t checksum = compute_checksum( + packetin.getDstAddress(), + packetin.getSrcAddress(), + NH_ICMP, + len, + output_buffer, + len); + memcpy(output_buffer + 2, &checksum, 2); + return len; + + } else if (type == ICMP_RADV_TYPE) { /* if it is IVMPc6 Router Advertisement, then... */ + ICMPv6_RADV* radv = (ICMPv6_RADV*)input_buffer; + HOP_LIMIT = (radv->cur_hop_limit != 0) ? radv->cur_hop_limit : 64; + + for ( + ICMPv6_RADV_Option* option = (ICMPv6_RADV_Option*) (radv + 1); + option != input_buffer + len; + option = (ICMPv6_RADV_Option*) ( ((char*)(option)) + option->len )) { + digitalWrite(13, !digitalRead(13)); + if (option->type == 3 && option->len == 4) { /* if it is a ICMPv6 Prefix Information, then... */ + ICMPv6_RADV_PrefixInformation* p = (ICMPv6_RADV_PrefixInformation*) option; + + if (p->prefix_length != 64) continue; /* only /64 prefix is supported */ + if (! (p->la_flags & 0x40)) continue; /* only SLAAC is supported */ + memcpy(&coppino::address, p->prefix, 8); /* set prefix -- leave host bits unchanged */ + break; + } + } + + return -1; + } return -1; }