├── examples ├── NanodeUDPServer │ ├── .gitignore │ ├── Makefile │ ├── udp-send.pl │ └── NanodeUDPServer.ino ├── Makefile ├── LinuxPacketPrinter │ ├── Makefile │ └── LinuxPacketPrinter.cpp ├── Minimal │ └── Minimal.ino ├── WebToggler │ └── eswt.css ├── Syslog │ └── Syslog.ino ├── MinimalStatic │ └── MinimalStatic.ino ├── PacketPrinter │ └── PacketPrinter.ino ├── MiniHTTPServer │ └── MiniHTTPServer.ino ├── TFTPServer │ ├── TFTPServer.ino │ └── CustomTFTPServer.h ├── SNTPClient │ └── ntp.h ├── PingClient │ └── PingClient.ino └── NanodeUDPClient │ └── NanodeUDPClient.ino ├── docs ├── tcp-server-sequence.png ├── generate-examples.pl ├── Makefile └── generate-keywords.pl ├── tests ├── packets │ ├── dns_res_error.hext │ ├── dns_res_no_aaaa.hext │ ├── ipv4_packet_valid.hext │ ├── dns_req_aelius.hext │ ├── dummy_hello_world.hext │ ├── ipv6_empty.hext │ ├── icmp6_neighbour_solicitation_dad_linklocal.hext │ ├── ipv6_unknown_protocol.hext │ ├── icmp6_unknown_type.hext │ ├── udp_syslog_hello_world.hext │ ├── udp_valid_hello.hext │ ├── icmp6_echo_request.hext │ ├── icmp6_echo_response.hext │ ├── udp_reply_oh_hi.hext │ ├── udp_valid_oh_hi.hext │ ├── icmp6_echo_response2.hext │ ├── udp_invalid_hello_wrong_protocol.hext │ ├── udp_multicast.hext │ ├── icmp6_echo_request2.hext │ ├── icmp6_neighbour_solicitation_global.hext │ ├── udp_tftp_invalid_op.hext │ ├── icmp6_router_solicitation.hext │ ├── udp_tftp_ack_read.hext │ ├── udp_tftp_reply_ack.hext │ ├── udp_tftp_reply_not_found.hext │ ├── udp_tftp_reply_hello_world.hext │ ├── udp_tftp_write_hello_world.hext │ ├── udp_tftp_reply_invalid_op.hext │ ├── udp_tftp_read_foobar.hext │ ├── udp_tftp_read_hello_txt.hext │ ├── udp_tftp_write_hello_txt.hext │ ├── tcp_receive_rst.hext │ ├── tcp_send_rst.hext │ ├── icmp6_neighbour_solicitation_global2.hext │ ├── icmp6_neighbour_solicitation_linklocal.hext │ ├── tcp_send_syn_ack.hext │ ├── icmp6_neighbour_advertisement_global.hext │ ├── icmp6_neighbour_advertisement_global2.hext │ ├── icmp6_neighbour_advertisement_global3.hext │ ├── icmp6_neighbour_advertisement_global4.hext │ ├── icmp6_neighbour_advertisement_linklocal.hext │ ├── tcp_send_data_hello_world.hext │ ├── tcp_send_fin_ack.hext │ ├── http_response_plain_on.hext │ ├── udp_dns_request.hext │ ├── http_response_not_found.hext │ ├── http_response_redirect_foo_bar.hext │ ├── tcp_receive_ack.hext │ ├── tcp_receive_fin_ack.hext │ ├── tcp_receive_data.hext │ ├── tcp_receive_syn.hext │ ├── http_get_root.hext │ ├── http_post_output1_off.hext │ ├── udp_dns_response.hext │ ├── icmp6_unrecognized_next_header.hext │ ├── icmp6_port_unreachable.hext │ ├── dns_res_aelius.hext │ ├── icmp6_router_advertisment.hext │ ├── dns_res_long.hext │ └── icmp6_router_advertisment_with_dns.hext ├── libarduino │ ├── Stream.h │ ├── Buffer.cpp │ ├── Stream.cpp │ ├── SPI.cpp │ ├── Buffer.h │ ├── Progmem.h │ ├── Arduino.cpp │ ├── Print.h │ ├── Arduino.h │ ├── SPI.h │ └── Print.cpp ├── hext.hh ├── hext.h ├── 10_check_endian.tc ├── ipv6checksum.cpp ├── Makefile ├── 70_check_syslog.tc ├── 70_check_dns.tc ├── 20_check_mac_address.tc └── 70_check_http.tc ├── .gitignore ├── library.properties ├── ping6-linklocal.rb ├── CONTRIBUTING.md ├── Makefile ├── src ├── esendian.h ├── Syslog.cpp ├── LinuxSocket.h ├── tcp.h ├── UDPSocket.cpp ├── util.h ├── TCPServer.h ├── dns.h ├── MACAddress.cpp ├── util.cpp ├── PingClient.cpp ├── IPv6Packet.cpp ├── UDPSocket.h ├── TCPServer.cpp ├── Syslog.h ├── TFTPServer.h ├── MACAddress.h ├── dummy.h ├── enc28j60.h ├── HTTPServer.cpp ├── Socket.cpp ├── dummy.cpp └── LinuxSocket.cpp ├── LICENSE.md ├── NEWS.md ├── keywords.txt └── README.md /examples/NanodeUDPServer/.gitignore: -------------------------------------------------------------------------------- 1 | build-* 2 | -------------------------------------------------------------------------------- /docs/tcp-server-sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/njh/EtherSia/HEAD/docs/tcp-server-sequence.png -------------------------------------------------------------------------------- /tests/packets/dns_res_error.hext: -------------------------------------------------------------------------------- 1 | 2a 45 81 83 00 01 00 00 00 00 00 00 07 69 6e 76 2 | 61 6c 69 64 07 65 78 61 6d 70 6c 65 03 63 6f 6d 3 | 00 00 1c 00 01 4 | -------------------------------------------------------------------------------- /tests/packets/dns_res_no_aaaa.hext: -------------------------------------------------------------------------------- 1 | e0 10 81 80 00 01 00 01 00 00 00 00 03 77 77 77 2 | 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 01 00 3 | 01 c0 0c 00 01 00 01 00 00 c5 83 00 04 5d b8 d8 4 | 22 5 | -------------------------------------------------------------------------------- /examples/NanodeUDPServer/Makefile: -------------------------------------------------------------------------------- 1 | # Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile 2 | 3 | BOARD_TAG = atmega328 4 | ARDUINO_PORT = /dev/ttyUSB1 5 | ARDUINO_LIBS = SPI EtherSia NanodeUNIO 6 | 7 | include /usr/share/arduino/Arduino.mk 8 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | EXAMPLES := $(wildcard */*.ino) 2 | ARDUINO := arduino 3 | ARDUINO_FLAGS := --verify --board arduino:avr:uno 4 | 5 | all: $(EXAMPLES) 6 | 7 | $(EXAMPLES): 8 | $(ARDUINO) $(ARDUINO_FLAGS) $@ 9 | @echo 10 | 11 | .PHONY: all $(EXAMPLES) 12 | -------------------------------------------------------------------------------- /docs/generate-examples.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | 5 | my @examples = split(/\n/, `git ls-files ../examples/*/*.ino`); 6 | foreach my $example (@examples) { 7 | $example =~ s|\.\./examples/\w+/||i; 8 | print "/** \@example $example */\n"; 9 | print "/** \@file $example */\n"; 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.dSYM 4 | *.bak 5 | 6 | coverage.info 7 | coverage/* 8 | *.gcda 9 | *.gcno 10 | 11 | docs/html 12 | docs/doxygen.log 13 | docs/examples.txt 14 | docs/tags.xml 15 | 16 | examples/LinuxPacketPrinter/LinuxPacketPrinter 17 | 18 | tests/*_check_*.cpp 19 | tests/*_check_*.cmd 20 | tests/ipv6checksum 21 | -------------------------------------------------------------------------------- /tests/packets/ipv4_packet_valid.hext: -------------------------------------------------------------------------------- 1 | ca:2f:6d:70:f9:5f # Ethernet Destination 2 | 02:c0:de:02:02:02 # Ethernet Source 3 | 0800 # EtherType (IPv4) 4 | 5 | 45 00 6 | 00 32 56 66 00 00 40 11 a2 e4 c0 a8 00 1f c0 a8 7 | 00 01 cf 89 00 35 00 1e 71 70 c9 37 01 00 00 01 8 | 00 00 00 00 00 00 01 74 02 63 6f 00 00 01 00 01 9 | -------------------------------------------------------------------------------- /tests/libarduino/Stream.h: -------------------------------------------------------------------------------- 1 | #ifndef Stream_h 2 | #define Stream_h 3 | 4 | #include "Print.h" 5 | 6 | 7 | class Stream : public Print { 8 | 9 | public: 10 | void begin(int baud); 11 | virtual size_t write(uint8_t chr); 12 | virtual size_t write(const uint8_t *buffer, size_t size); 13 | }; 14 | 15 | extern Stream Serial; 16 | extern Stream Stdout; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /tests/libarduino/Buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | 3 | 4 | Buffer::Buffer(size_t size) 5 | { 6 | _buffer = (char*)malloc(size); 7 | memset(_buffer, 0, size); 8 | _allocated = size; 9 | _pos = 0; 10 | } 11 | 12 | Buffer::~Buffer() 13 | { 14 | free(_buffer); 15 | } 16 | 17 | size_t Buffer::write(uint8_t chr) 18 | { 19 | _buffer[_pos++] = chr; 20 | return 1; 21 | } 22 | -------------------------------------------------------------------------------- /tests/libarduino/Stream.cpp: -------------------------------------------------------------------------------- 1 | #include "Stream.h" 2 | 3 | #include 4 | 5 | Stream Serial; 6 | Stream Stdout; 7 | 8 | void Stream::begin(int) 9 | { 10 | } 11 | 12 | size_t Stream::write(uint8_t chr) 13 | { 14 | return fwrite(&chr, 1, 1, stdout); 15 | } 16 | 17 | size_t Stream::write(const uint8_t *buffer, size_t size) 18 | { 19 | return fwrite(buffer, 1, size, stdout); 20 | } 21 | -------------------------------------------------------------------------------- /tests/libarduino/SPI.cpp: -------------------------------------------------------------------------------- 1 | #include "SPI.h" 2 | 3 | void SPIClass::begin() {} 4 | 5 | uint8_t SPIClass::transfer(uint8_t val) { return val; } 6 | 7 | void SPIClass::end() {} 8 | 9 | void SPIClass::setBitOrder(uint8_t) {} 10 | void SPIClass::setDataMode(uint8_t) {} 11 | void SPIClass::setClockDivider(uint8_t) {} 12 | void SPIClass::beginTransaction(SPISettings /*settings*/) {} 13 | void SPIClass::endTransaction() {} 14 | -------------------------------------------------------------------------------- /tests/packets/dns_req_aelius.hext: -------------------------------------------------------------------------------- 1 | # Request for AAAA ipv6.aelius.com 2 | 3 | 1234 # Request ID 4 | 0100 # Flags (Recursion Desired) 5 | 0001 # QD: Question Count 6 | 0000 # AN: Answer Count 7 | 0000 # NS: Name Server Count 8 | 0000 # AR: Additional Record Count 9 | 10 | # Response to question 11 | 04 "ipv6" 12 | 06 "aelius" 13 | 03 "com" 14 | 00 15 | 16 | 001c # Type 28 - IP6 Address 17 | 0001 # Class 1 - Internet 18 | -------------------------------------------------------------------------------- /tests/packets/dummy_hello_world.hext: -------------------------------------------------------------------------------- 1 | ca:2f:6d:70:f9:5f # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 000E # Length (14 bytes) 7 | EE # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:1234:0000:0000:0000:0000:0000:0001 # IPv6 Source Address 11 | 2001:4321:0000:0000:0000:0000:0000:1234 # IPv6 Destination Address 12 | 13 | "Hello World\r\n" 14 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=EtherSia 2 | version=2.4.0 3 | author=Nicholas Humfrey 4 | maintainer=Nicholas Humfrey 5 | sentence=IPv6 library for the ENC28J60, W5500 or W5100 Ethernet controllers 6 | paragraph=A library to allow network communication using IPv6 over Ethernet. Supports a minimal HTTP Server, UDP Client and Server, DNS lookup and SLAAC (Stateless Auto-configuration). 7 | category=Communication 8 | url=http://github.com/njh/EtherSia 9 | license=BSD-3-Clause 10 | architectures=avr,megaavr 11 | -------------------------------------------------------------------------------- /tests/libarduino/Buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef Buffer_h 2 | #define Buffer_h 3 | 4 | #include "Print.h" 5 | 6 | 7 | class Buffer : public Print { 8 | 9 | public: 10 | Buffer(size_t size=2048); 11 | ~Buffer(); 12 | 13 | virtual size_t write(uint8_t chr); 14 | 15 | uint16_t size() { return _pos; }; 16 | 17 | operator uint8_t*() { return (uint8_t*)_buffer; }; 18 | operator char*() { return _buffer; }; 19 | 20 | protected: 21 | char *_buffer; 22 | size_t _pos; 23 | size_t _allocated; 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /tests/packets/ipv6_empty.hext: -------------------------------------------------------------------------------- 1 | 36:b4:40:75:3b:a6 # Ethernet Destination 2 | a6:69:c0:80:da:3b # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0000 # Length (0 bytes) 7 | fe # Protocol (0xfe = Use for experimentation and testing) 8 | 40 # Hop Limit 9 | 10 | fe80:0000:0000:0000:a469:c0ff:fe80:da3b # IPv6 Source Address 11 | fe80:0000:0000:0000:34b4:40ff:fe75:3ba6 # IPv6 Destination Address 12 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | html: doxygen 2 | tags.xml: doxygen 3 | 4 | examples.txt: ../examples/*/*.ino 5 | ./generate-examples.pl > examples.txt 6 | 7 | doxygen: examples.txt 8 | @doxygen Doxyfile 9 | @echo "" 10 | @echo "Done, error log follows:" 11 | @echo "" 12 | @cat doxygen.log 13 | 14 | ../keywords.txt: tags.xml 15 | ./generate-keywords.pl $< > $@ 16 | 17 | clean: 18 | rm -rf html 19 | rm -f doxygen.log 20 | rm -f examples.txt 21 | 22 | upload: doxygen 23 | rsync -avz -e ssh html/ njh@www.aelius.com:~/public_html/ethersia/ 24 | 25 | .PHONY: html doxygen clean upload 26 | -------------------------------------------------------------------------------- /tests/libarduino/Progmem.h: -------------------------------------------------------------------------------- 1 | #ifndef Progmem_h 2 | #define Progmem_h 3 | 4 | class __FlashStringHelper; 5 | 6 | #define PROGMEM 7 | #define PSTR(x) ((const char*)(x)) 8 | #define F(x) (reinterpret_cast(x)) 9 | #define pgm_read_byte_near(x) *(x) 10 | #define memcpy_P(dst, src, n) memcpy(dst, src, n) 11 | #define memcmp_P(p1, p2, n) memcmp(p1, p2, n) 12 | #define strcmp_P(s1, s2) strcmp(s1, s2) 13 | #define strcpy_P(dst, src) strcpy(dst, src) 14 | #define strncpy_P(dst, src, len) strncpy(dst, src, len) 15 | #define strlen_P(str) strlen(str) 16 | #define pgm_read_byte(addr) *(addr) 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /tests/libarduino/Arduino.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | 3 | uint32_t millis( void ) {return 0;} 4 | uint32_t micros( void ) {return 100;} 5 | void delay(uint32_t /* msec */) {} 6 | void delayMicroseconds(uint32_t /* us */) {} 7 | 8 | void pinMode(uint8_t, uint8_t) {} 9 | void digitalWrite(uint8_t, uint8_t) {} 10 | int digitalRead(uint8_t) {return 0;} 11 | 12 | long random() {return 0x55555555;} 13 | long random(long max) {return max/2;} 14 | long random(long min, long max) {return ((max-min)/2)+min;} 15 | void randomSeed(unsigned long) {} 16 | 17 | boolean isWhitespace(int c) 18 | { 19 | return (isblank (c) == 0 ? false : true); 20 | } 21 | -------------------------------------------------------------------------------- /tests/packets/icmp6_neighbour_solicitation_dad_linklocal.hext: -------------------------------------------------------------------------------- 1 | 33:33:ff:70:f9:5f # Ethernet Destination 2 | ca:2f:6d:70:f9:5f # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0018 # Length 7 | 3a # IP Protocol 8 | ff # Hop Limit 9 | 10 | 0000:0000:0000:0000:0000:0000:0000:0000 # Source Address 11 | ff02:0000:0000:0000:0000:0001:ff70:f95f # Destination Address 12 | 13 | 87 00 54 57 00 00 00 00 # ICMPv6 header 14 | fe80:0000:0000:0000:c82f:6dff:fe70:f95f # ICMPv6 Target Address 15 | -------------------------------------------------------------------------------- /tests/packets/ipv6_unknown_protocol.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 00 0d # Length (16 bytes) 7 | fd # Protocol - Use for experimentation and testing (RFC3692) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | 14 | # Our made-up protocol 15 | 2d53 # Checksum 16 | "Hello World" 17 | -------------------------------------------------------------------------------- /examples/NanodeUDPServer/udp-send.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # Perl Script to turn the RED LED on the Nanode On and Off 4 | # 5 | 6 | use IO::Socket::INET6; 7 | use Socket6; 8 | 9 | my $socket = new IO::Socket::INET6( 10 | PeerAddr => "fe80::0204:a3ff:fe2c:2bb9%en0", 11 | PeerPort => 1234, 12 | Proto => 'udp' 13 | ) or die "ERROR in Socket Creation: $!\n"; 14 | 15 | for (my $i=1; $i <= 10; $i++) { 16 | 17 | $socket->send("on") or die "Error ending packet: $!\n"; 18 | 19 | sleep(1); 20 | 21 | $socket->send("off") or die "Error ending packet: $!\n"; 22 | 23 | sleep(1); 24 | 25 | } 26 | 27 | $socket->close(); 28 | -------------------------------------------------------------------------------- /tests/hext.hh: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | C++ wrapper class to load a hext file into memory 4 | 5 | Copyright 2016 Nicholas Humfrey 6 | 7 | */ 8 | 9 | #ifndef HEXT_HH 10 | #define HEXT_HH 11 | 12 | extern "C" { 13 | #include "hext.h" 14 | } 15 | 16 | class HextFile { 17 | public: 18 | HextFile(const char* filename) { 19 | length = hext_filename_to_buffer(filename, buffer, buffer_size); 20 | if (length <= 0) { 21 | fprintf(stderr, "Error: failed to load file: %s\n", filename); 22 | } 23 | } 24 | 25 | static const int buffer_size = 2048; 26 | int length = 0; 27 | uint8_t buffer[buffer_size]; 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /tests/packets/icmp6_unknown_type.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 00 0d # Length (13 bytes) 7 | 3a # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | 64 # ICMPv6 Type 14 | 00 # ICMPv6 Code 15 | 07d4 # Checksum 16 | 17 | 00 01 02 03 04 05 06 07 08 18 | -------------------------------------------------------------------------------- /tests/packets/udp_syslog_hello_world.hext: -------------------------------------------------------------------------------- 1 | ca:2f:6d:70:f9:5f # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0023 # Length (35 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:1234:0000:0000:0000:0000:0000:0001 11 | 2001:4321:0000:0000:0000:0000:0000:0514 12 | 13 | 61a8 # UDP Source Port 14 | 0202 # UDP Destination Port 15 | 0023 # Length (35 bytes) 16 | d716 # Checksum 17 | "<134>EtherSia: Hello World\0" 18 | -------------------------------------------------------------------------------- /tests/packets/udp_valid_hello.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 03 b1 b7 # IPv6 header 6 | 000d # Length (13 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | fa06 # UDP Source Port 14 | 03f0 # UDP Destination Port 15 | 000d # Length (32 bytes) 16 | 5e37 # Checksum 17 | "Hello" # UDP Payload 18 | -------------------------------------------------------------------------------- /tests/packets/icmp6_echo_request.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 06 f6 54 # IPv6 header 6 | 00 10 # Length (16 bytes) 7 | 3a # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | 80 # ICMPv6 Echo Request 14 | 00 # ICMPv6 Code 15 | 33f7 # Checksum 16 | 1d3a # Identifier 17 | 0021 # Sequence 18 | 58 07 ed de 00 0a 68 9e # Data 19 | -------------------------------------------------------------------------------- /tests/packets/icmp6_echo_response.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0010 # Length (16 bytes) 7 | 3a # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 81 # ICMPv6 Echo Reply 14 | 00 # ICMPv6 Code 15 | 32 f7 # Checksum 16 | 1d 3a # Identifier 17 | 00 21 # Sequence 18 | 58 07 ed de 00 0a 68 9e # Data 19 | -------------------------------------------------------------------------------- /tests/packets/udp_reply_oh_hi.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 000e # Length (14 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 03f0 # UDP Source Port 14 | fa06 # UDP Destination Port 15 | 000e # Length (14 bytes) 16 | a915 # Checksum 17 | "Oh hi!" # UDP Payload 18 | -------------------------------------------------------------------------------- /tests/packets/udp_valid_oh_hi.hext: -------------------------------------------------------------------------------- 1 | ca:2f:6d:70:f9:5f # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 000e # Length (14 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:41c8:0051:07cf:0000:0000:0000:0006 # IPv6 Destination Address 12 | 13 | 61a8 # UDP Source Port 14 | 138c # UDP Destination Port 15 | 000e # Length (14 bytes) 16 | 4f49 # Checksum 17 | "Oh hi!" # UDP Payload 18 | -------------------------------------------------------------------------------- /tests/packets/icmp6_echo_response2.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | ca:2f:6d:70:f9:5f # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0010 # Length (16 bytes) 7 | 3a # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:41c8:0051:07cf:0000:0000:0000:0006 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | 81 # ICMPv6 Echo Reply 14 | 00 # ICMPv6 Code 15 | 71a7 # Checksum 16 | 5555 # Identifier 17 | 0000 # Sequence 18 | 19 | 55 55 55 55 55 55 55 55 # Data 20 | -------------------------------------------------------------------------------- /tests/packets/udp_invalid_hello_wrong_protocol.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 03 b1 b7 # IPv6 header 6 | 000d # Length (13 bytes) 7 | 12 # !!! Protocol (MUX) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | fa06 # UDP Source Port 14 | 03f0 # UDP Destination Port 15 | 000d # Length (32 bytes) 16 | 5e36 # Checksum 17 | "Hello" # UDP Payload 18 | -------------------------------------------------------------------------------- /tests/packets/udp_multicast.hext: -------------------------------------------------------------------------------- 1 | 33:33:00:00:00:01 # Ethernet Destination 2 | ca:2f:6d:70:f9:5f # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0017 # Length (23 bytes) 7 | 11 # UDP Protocol 8 | ff # Hop Limit 9 | 10 | 2001:0000:0000:0000:0000:0000:0000:0001 # IPv6 Source Address 11 | ff02:0000:0000:0000:0000:0000:0000:0001 # IPv6 Destination Address 12 | 13 | 61a8 # UDP Source Port 14 | 0be0 # UDP Destination Port 15 | 0017 # Length (23 bytes) 16 | 7bde # Checksum 17 | "Hello All Nodes" # UDP Payload 18 | -------------------------------------------------------------------------------- /tests/packets/icmp6_echo_request2.hext: -------------------------------------------------------------------------------- 1 | ca:2f:6d:70:f9:5f # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0010 # Length (16 bytes) 7 | 3a # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:41c8:0051:07cf:0000:0000:0000:0006 # IPv6 Destination Address 12 | 13 | 80 # ICMPv6 Echo Request 14 | 00 # ICMPv6 Code 15 | 72a7 # Checksum 16 | 5555 # Identifier 17 | 0000 # Sequence 18 | 19 | 55 55 55 55 55 55 55 55 # Data 20 | -------------------------------------------------------------------------------- /tests/packets/icmp6_neighbour_solicitation_global.hext: -------------------------------------------------------------------------------- 1 | 33:33:ff:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 06 f6 54 # IPv6 header 6 | 0018 # Length (24 bytes) 7 | 3a # ICMPv6 Protocol 8 | ff # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | ff02:0000:0000:0000:0000:0001:ff2c:2bb9 # IPv6 Destination Address 12 | 13 | 87 # ICMPv6 neighbour solicitation (135) 14 | 00 # ICMPv6 Code 15 | ceee # Checksum 16 | 00 00 00 00 # Reserved 17 | 18 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 19 | -------------------------------------------------------------------------------- /ping6-linklocal.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Ruby script to make it easier to ping an IPv6 link local address 4 | # 5 | # Usage: ./ping6-linklocal.rb 6 | # 7 | 8 | require 'socket' 9 | 10 | mac = ARGV[0] || '00:04:A3:2C:2B:B9' 11 | ifname = ARGV[1] 12 | 13 | if ifname.nil? 14 | ifname = Socket.getifaddrs.reject { |ifaddr| 15 | ifaddr.addr.nil? or 16 | ifaddr.addr.afamily != Socket::PF_INET6 or 17 | (ifaddr.flags & Socket::IFF_LOOPBACK) != 0 18 | }.first.name 19 | end 20 | 21 | parts = mac.strip.split(':').map {|m| m.to_i(16)} 22 | ip6addr = sprintf("fe80::%2.2x%2.2x:%2.2xff:fe%2.2x:%2.2x%2.2x", 23 | parts[0] ^ 0x02, parts[1], 24 | parts[2], parts[3], 25 | parts[4], parts[5] 26 | ) 27 | 28 | exec('ping6', '-I', ifname, ip6addr) 29 | -------------------------------------------------------------------------------- /tests/packets/udp_tftp_invalid_op.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 03 b1 b7 # IPv6 header 6 | 000e # Length (14 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | fa06 # UDP Source Port 14 | 0045 # UDP Destination Port (69) 15 | 000e # Length (14 bytes) 16 | 74a3 # Checksum 17 | 18 | 00ff # Invalid TFTP operation 19 | 1010 20 | ffff 21 | -------------------------------------------------------------------------------- /tests/packets/icmp6_router_solicitation.hext: -------------------------------------------------------------------------------- 1 | 33:33:00:00:00:02 # Ethernet Destination 2 | ca:2f:6d:70:f9:5f # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0010 # Length 7 | 3a # IP Protocol 8 | ff # Hop Limit 9 | 10 | fe80:0000:0000:0000:c82f:6dff:fe70:f95f # IPv6 Source Address 11 | ff02:0000:0000:0000:0000:0000:0000:0002 # IPv6 Destination Address 12 | 13 | 85 # ICMPv6 router solicitation (133) 14 | 00 # ICMPv6 Code 15 | 1d2e # Checksum 16 | 17 | 00 00 00 # Reserved 18 | 00 01 01 # ICMPv6 header 19 | ca:2f:6d:70:f9:5f # ICMPv6 Source Address 20 | -------------------------------------------------------------------------------- /tests/packets/udp_tftp_ack_read.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 03 b1 b7 # IPv6 header 6 | 000c # Length (12 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | fa06 # UDP Source Port 14 | 61a8 # UDP Destination Port (25000) 15 | 000c # Length (12 bytes) 16 | 244e # Checksum 17 | 18 | 0004 # TFTP Ack operation 19 | 0001 # Block Number 20 | -------------------------------------------------------------------------------- /tests/packets/udp_tftp_reply_ack.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 000c # Length (12 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 61a8 # UDP Source Port 14 | fa06 # UDP Destination Port (25000) 15 | 000c # Length (12 bytes) 16 | 244e # Checksum 17 | 18 | 0004 # TFTP Ack operation 19 | 0001 # Block Number 20 | -------------------------------------------------------------------------------- /tests/packets/udp_tftp_reply_not_found.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0016 # Length (22 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 0045 # UDP Source Port (69) 14 | fa06 # UDP Destination Port 15 | 0016 # Length (22 bytes) 16 | a32e # Checksum 17 | 18 | 0005 # TFTP Error 19 | 0001 # File Not Found 20 | 21 | "Not Found\0" 22 | -------------------------------------------------------------------------------- /tests/packets/udp_tftp_reply_hello_world.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0018 # Length (24 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 61a8 # UDP Source Port (69) 14 | fa06 # UDP Destination Port 15 | 0018 # Length (24 bytes) 16 | d25e # Checksum 17 | 18 | 0003 # TFTP Data 19 | 0001 # Block Number 20 | 21 | "Hello World\n" 22 | -------------------------------------------------------------------------------- /tests/packets/udp_tftp_write_hello_world.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 03 b1 b7 # IPv6 header 6 | 0018 # Length (24 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | fa06 # UDP Source Port 14 | 61a8 # UDP Destination Port (25000) 15 | 0018 # Length (24 bytes) 16 | d25e # Checksum 17 | 18 | 0003 # TFTP Data 19 | 0001 # Block Number 20 | 21 | "Hello World\n" 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to EtherSia 2 | ======================== 3 | 4 | Contributions to the EtherSia codebase are welcome using the usual Github pull request workflow. 5 | 6 | Please remember that memory, particularly RAM, is often limited on Arduino, so try and be efficient when adding new features. 7 | 8 | 9 | Code Style 10 | ---------- 11 | 12 | When making contributions, please use the following code style to keep the codebase consistent: 13 | 14 | * Indent using *4 spaces* not tabs 15 | * When placing an opening brace on the same line, there should be one space before it 16 | * Closing braces should be broken from the preceding line 17 | 18 | If in doubt, this is the same as the default [astyle] settings, so use the astyle tool to check your code. 19 | 20 | 21 | 22 | [astyle]: http://astyle.sourceforge.net/ 23 | -------------------------------------------------------------------------------- /tests/packets/udp_tftp_reply_invalid_op.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0017 # Length (23 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 0045 # UDP Source Port (69) 14 | fa06 # UDP Destination Port 15 | 0017 # Length (23 bytes) 16 | acd3 # Checksum 17 | 18 | 0005 # TFTP Error 19 | 0004 # Illegal TFTP operation 20 | 21 | "Illegal Op\0" 22 | -------------------------------------------------------------------------------- /tests/libarduino/Print.h: -------------------------------------------------------------------------------- 1 | #ifndef Print_h 2 | #define Print_h 3 | 4 | #include "Progmem.h" 5 | 6 | #include 7 | #include 8 | 9 | #define DEC 10 10 | #define HEX 16 11 | #define OCT 8 12 | #define BIN 2 13 | 14 | class Print { 15 | 16 | public: 17 | size_t print(const char str[]); 18 | size_t print(const __FlashStringHelper* ifsh); 19 | size_t print(char c); 20 | size_t print(int i, int base = DEC); 21 | size_t print(unsigned int i, int base = DEC); 22 | 23 | size_t println(const char str[]); 24 | size_t println(const __FlashStringHelper* ifsh); 25 | size_t println(char c); 26 | size_t println(int i, int = DEC); 27 | size_t println(unsigned int i, int base = DEC); 28 | size_t println(void); 29 | 30 | virtual size_t write(uint8_t chr) = 0; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | docs: 2 | $(MAKE) -C docs 3 | 4 | examples: 5 | $(MAKE) -C examples 6 | 7 | test: 8 | $(MAKE) -C tests test 9 | 10 | coverage: coverage-html 11 | 12 | coverage.info: 13 | lcov --zerocounters --directory src 14 | $(MAKE) -C tests COVERAGE_CFLAGS="-fprofile-arcs -ftest-coverage" test 15 | lcov --capture --directory src --output-file coverage.info --no-external 16 | lcov --remove coverage.info --output-file coverage.info `pwd`/src/libarduino/* 17 | 18 | coverage-html: coverage.info 19 | mkdir -p coverage 20 | genhtml -t "EtherSia Coverage Report" --output-directory coverage $< 21 | 22 | clean: 23 | $(MAKE) -C docs clean 24 | $(MAKE) -C tests clean 25 | find . -name "*.gcno" -delete 26 | find . -name "*.gcda" -delete 27 | rm -Rf coverage.info coverage/ 28 | 29 | .PHONY: docs examples test coverage coverage-html clean 30 | -------------------------------------------------------------------------------- /tests/packets/udp_tftp_read_foobar.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 03 b1 b7 # IPv6 header 6 | 001b # Length (27 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | fa06 # UDP Source Port 14 | 0045 # UDP Destination Port (69) 15 | 001b # Length (27 bytes) 16 | de12 # Checksum 17 | 18 | 0001 # TFTP Read operation 19 | "foobar.txt" 00 # TFTP Filename 20 | "octet" 00 # TFTP Type 21 | -------------------------------------------------------------------------------- /tests/packets/udp_tftp_read_hello_txt.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 03 b1 b7 # IPv6 header 6 | 001a # Length (26 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | fa06 # UDP Source Port 14 | 0045 # UDP Destination Port (69) 15 | 001a # Length (26 bytes) 16 | 0157 # Checksum 17 | 18 | 0001 # TFTP Read operation 19 | "hello.txt" 00 # TFTP Filename 20 | "octet" 00 # TFTP Type 21 | -------------------------------------------------------------------------------- /tests/packets/udp_tftp_write_hello_txt.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 03 b1 b7 # IPv6 header 6 | 001a # Length (26 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | fa06 # UDP Source Port 14 | 0045 # UDP Destination Port (69) 15 | 001a # Length (26 bytes) 16 | 0156 # Checksum 17 | 18 | 0002 # TFTP Write operation 19 | "hello.txt" 00 # TFTP Filename 20 | "octet" 00 # TFTP Type 21 | -------------------------------------------------------------------------------- /tests/packets/tcp_receive_rst.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 02 21 ca # IPv6 header 6 | 0014 # Length (32 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | c863 # TCP Source port number (51299) 14 | 0050 # TCP Destination port number (80) 15 | 16 | 00000000 # TCP Sequence number 17 | 81cb4091 # TCP Acknowledgement number 18 | 50 # TCP Header length (20 bytes) 19 | 14 # TCP Flags (FIN and ACK) 20 | 0000 # TCP Window size 21 | a4ec # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | -------------------------------------------------------------------------------- /examples/LinuxPacketPrinter/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -std=c++11 -Wall -Wextra -pedantic 2 | CFLAGS += -I../../src -I../../tests/libarduino 3 | 4 | LIBARDUINO_SOURCES=$(wildcard ../../tests/libarduino/*.cpp) 5 | LIBARDUINO_OBJECTS=$(LIBARDUINO_SOURCES:%.cpp=%.o) 6 | 7 | LIBETHERSIA_SOURCES=$(wildcard ../../src/*.cpp) 8 | LIBETHERSIA_OBJECTS=$(LIBETHERSIA_SOURCES:%.cpp=%.o) 9 | 10 | %.o: %.cpp 11 | $(CXX) $(CFLAGS) -c -o $@ $< 12 | 13 | LinuxPacketPrinter: LinuxPacketPrinter.o libarduino.a libethersia.a 14 | $(CXX) -o $@ $< -L. -lethersia -larduino $(CFLAGS) 15 | 16 | libarduino.a: $(LIBARDUINO_OBJECTS) 17 | $(AR) rcs $@ $^ 18 | 19 | libethersia.a: $(LIBETHERSIA_OBJECTS) 20 | $(AR) rcs $@ $^ 21 | 22 | clean: 23 | rm -f libarduino.a $(LIBARDUINO_OBJECTS) 24 | rm -f libethersia.a $(LIBETHERSIA_OBJECTS) 25 | rm -f LinuxPacketPrinter LinuxPacketPrinter.o 26 | 27 | .PHONY: clean 28 | -------------------------------------------------------------------------------- /tests/packets/tcp_send_rst.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0014 # Length (20 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 0050 # TCP Source port number (80) 14 | cfed # TCP Destination port number (53229) 15 | 16 | 00000000 # TCP Sequence number 17 | 6fbb7777 # TCP Acknowledgement number 18 | 50 # TCP Header length (20 bytes) 19 | 14 # TCP Flags (RST, ACK) 20 | 0000 # TCP Window size (0 bytes) 21 | 788c # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | -------------------------------------------------------------------------------- /tests/packets/icmp6_neighbour_solicitation_global2.hext: -------------------------------------------------------------------------------- 1 | 33:33:ff:00:50:00 # Ethernet Destination 2 | ca:2f:6d:70:f9:5f # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0020 # Length (32 bytes) 7 | 3a # Protocol 8 | ff # Hop Limit 9 | 10 | 2001:1234:0000:0000:0000:0000:0000:0001 # IPv6 Source Address 11 | ff02:0000:0000:0000:0000:0001:ff00:5000 # IPv6 Destination Address 12 | 13 | 87 # ICMPv6 neighbour solicitation (135) 14 | 00 # ICMPv6 Code 15 | 4433 # Checksum 16 | 00 00 00 00 # Reserved 17 | 18 | 2001:1234:0000:0000:0000:0000:0000:5000 19 | 20 | 01 # Option: Source Link Address 21 | 01 # Option: Length (8 bytes) 22 | ca:2f:6d:70:f9:5f # Target address 23 | -------------------------------------------------------------------------------- /tests/packets/icmp6_neighbour_solicitation_linklocal.hext: -------------------------------------------------------------------------------- 1 | 33:33:ff:ba:66:2d # Ethernet Destination 2 | ca:2f:6d:70:f9:5f # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0020 # Length (32 bytes) 7 | 3a # Protocol 8 | ff # Hop Limit 9 | 10 | fe80:0000:0000:0000:c82f:6dff:fe70:f95f # IPv6 Source Address 11 | ff02:0000:0000:0000:0000:0001:ffba:662d # IPv6 Destination Address 12 | 13 | 87 # ICMPv6 neighbour solicitation (135) 14 | 00 # ICMPv6 Code 15 | bca1 # Checksum 16 | 00 00 00 00 # Reserved 17 | 18 | fe80:0000:0000:0000:082c:8cff:feba:662d 19 | 20 | 01 # Option: Source Link Address 21 | 01 # Option: Length (8 bytes) 22 | ca:2f:6d:70:f9:5f # Target address 23 | -------------------------------------------------------------------------------- /tests/packets/tcp_send_syn_ack.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0018 # Length (24 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 0050 # TCP Source port number (80) 14 | cfed # TCP Destination port number (53229) 15 | 16 | 55555555 # TCP Sequence number 17 | 6fbb7777 # TCP Acknowledgement number 18 | 60 # TCP Header length (24 bytes) 19 | 12 # TCP Flags (SYN, ACK) 20 | 01ea # TCP Window size (490 bytes) 21 | b807 # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 02 04 01ea # Maximum segment size: 490 bytes 25 | -------------------------------------------------------------------------------- /tests/packets/icmp6_neighbour_advertisement_global.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0020 # Length (16 bytes) 7 | 3a # Protocol 8 | ff # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 88 # ICMPv6 neighbour advertisement (136) 14 | 00 # ICMPv6 Code 15 | ee71 # Checksum 16 | 40 # Flags: solicited 17 | 00 00 00 # Reserved 18 | 19 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 20 | 21 | 02 # Option: Target Link Address 22 | 01 # Option Length (8 bytes) 23 | 00:04:a3:2c:2b:b9 # Target address 24 | -------------------------------------------------------------------------------- /tests/packets/icmp6_neighbour_advertisement_global2.hext: -------------------------------------------------------------------------------- 1 | ca:2f:6d:70:f9:5f # Ethernet Destination 2 | 01:02:03:04:05:06 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0020 # Length (16 bytes) 7 | 3a # Protocol 8 | ff # Hop Limit 9 | 10 | 2001:1234:0000:0000:0000:0000:0000:5000 # IPv6 Source Address 11 | 2001:1234:0000:0000:0000:a000:0000:0001 # IPv6 Destination Address 12 | 13 | 88 # ICMPv6 neighbour advertisement (136) 14 | 00 # ICMPv6 Code 15 | 55f6 # Checksum 16 | 40 # Flags: solicited 17 | 00 00 00 # Reserved 18 | 19 | 2001:1234:0000:0000:0000:0000:0000:5000 20 | 21 | 02 # Option: Target Link Address 22 | 01 # Option Length (8 bytes) 23 | 01:02:03:04:05:06 # Target address 24 | -------------------------------------------------------------------------------- /tests/packets/icmp6_neighbour_advertisement_global3.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 06 f6 54 # IPv6 header 6 | 0020 # Length (16 bytes) 7 | 3a # Protocol 8 | ff # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | 88 # ICMPv6 neighbour advertisement (136) 14 | 00 # ICMPv6 Code 15 | d098 # Checksum 16 | 40 # Flags: solicited 17 | 00 00 00 # Reserved 18 | 19 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d 20 | 21 | 02 # Option: Target Link Address 22 | 01 # Option Length (8 bytes) 23 | a4:5e:60:da:58:9d # Target address 24 | -------------------------------------------------------------------------------- /tests/packets/icmp6_neighbour_advertisement_global4.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 06 f6 54 # IPv6 header 6 | 0020 # Length (16 bytes) 7 | 3a # Protocol 8 | ff # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0000:0000:0000:0001 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | 88 # ICMPv6 neighbour advertisement (136) 14 | 00 # ICMPv6 Code 15 | 8e43 # Checksum 16 | 40 # Flags: solicited 17 | 00 00 00 # Reserved 18 | 19 | 2001:08b0:ffd5:0003:0000:0000:0000:0001 20 | 21 | 02 # Option: Target Link Address 22 | 01 # Option Length (8 bytes) 23 | a4:5e:60:da:58:9d # Target address 24 | -------------------------------------------------------------------------------- /tests/packets/icmp6_neighbour_advertisement_linklocal.hext: -------------------------------------------------------------------------------- 1 | ca:2f:6d:70:f9:5f # Ethernet Destination 2 | 0a:2c:8c:ba:66:2d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0020 # Length (16 bytes) 7 | 3a # Protocol 8 | ff # Hop Limit 9 | 10 | fe80:0000:0000:0000:082c:8cff:feba:662d # IPv6 Source Address 11 | fe80:0000:0000:0000:14d3:bc4d:240b:5895 # IPv6 Destination Address 12 | 13 | 88 # ICMPv6 neighbour advertisement (136) 14 | 00 # ICMPv6 Code 15 | fb23 # Checksum 16 | 40 # Flags: solicited 17 | 00 00 00 # Reserved 18 | 19 | fe80:0000:0000:0000:082c:8cff:feba:662d 20 | 21 | 02 # Option: Target Link Address 22 | 01 # Option Length (8 bytes) 23 | 0a:2c:8c:ba:66:2d # Target address 24 | -------------------------------------------------------------------------------- /tests/packets/tcp_send_data_hello_world.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0023 # Length (24 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 0050 # TCP Source port number (80) 14 | e899 # TCP Destination port number (59545) 15 | 16 | eabf6f22 # TCP Sequence number 17 | bb55a9a1 # TCP Acknowledgement number 18 | 60 # TCP Header length (24 bytes) 19 | 19 # TCP Flags (ACK, FIN, PSH) 20 | 01ea # TCP Window size (490 bytes) 21 | 207f # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 02 04 01ea # Maximum segment size: 490 bytes 25 | 26 | "Hello World" 27 | -------------------------------------------------------------------------------- /src/esendian.h: -------------------------------------------------------------------------------- 1 | #ifndef EtherSia_Endian_H 2 | #define EtherSia_Endian_H 3 | 4 | /** Convert a 16-bit integer from host (little-endian) to network (big-endian) */ 5 | #ifndef htons 6 | #define htons(x) ( ((x&0x00FFU)<<8) | (((x&0xFF00U)>>8)) ) 7 | #endif 8 | 9 | /** Convert a 16-bit integer from network (big-endian) to host (little-endian) */ 10 | #ifndef ntohs 11 | #define ntohs(x) htons(x) 12 | #endif 13 | 14 | /** Convert a 32-bit integer from host (little-endian) to network (big-endian) */ 15 | #ifndef htonl 16 | #define htonl(x) ( \ 17 | ((x & 0xff000000U)>>24) | \ 18 | ((x & 0x0000ff00U)<<8) | \ 19 | ((x & 0x00ff0000U)>>8) | \ 20 | ((x & 0x000000ffU)<<24) \ 21 | ) 22 | #endif 23 | 24 | /** Convert a 32-bit integer from network (big-endian) to host (little-endian) */ 25 | #ifndef ntohl 26 | #define ntohl(x) htonl(x) 27 | #endif 28 | 29 | /** Convert two bytes to a 16-bit integer */ 30 | #define bytesToWord(high, low) \ 31 | (((uint16_t)high << 8) + low) 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /tests/hext.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef HEXT_H 4 | #define HEXT_H 5 | 6 | typedef int (*hext_read_cb)(void* data); 7 | typedef int (*hext_write_cb)(int c, void* data); 8 | 9 | /** Convert hext to binary using callbacks for reading and writing */ 10 | int hext_cb_to_cb(void* input, void* output, hext_read_cb read_cb, hext_write_cb write_cb); 11 | 12 | /** Convert hext to binary using streams for input and output */ 13 | int hext_stream_to_stream(FILE* input, FILE* output); 14 | 15 | /** Convert hext to binary using a filename for input and callback for output */ 16 | int hext_filename_to_cb(const char* filename, void* output, hext_write_cb write_cb); 17 | 18 | /** Convert hext to binary using a filename for input and stream for output */ 19 | int hext_filename_to_stream(const char* filename, FILE* output); 20 | 21 | /** Convert hext to binary using a filename for input and buffer for output */ 22 | int hext_filename_to_buffer(const char* filename, uint8_t *buffer, size_t buffer_len); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /tests/packets/tcp_send_fin_ack.hext: -------------------------------------------------------------------------------- 1 | a4 5e 60 da 58 9d # Ethernet Destination 2 | 00 04 a3 2c 2b b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0018 # Length (24 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 0050 # TCP Source port number (53006) 14 | cf0e # TCP Destination port number (80) 15 | 16 | 62c3dcc7 # TCP Sequence number 17 | 4932c804 # TCP Acknowledgement number 18 | 60 # TCP Header length (32 bytes) 19 | 11 # TCP Flags (FIN and ACK) 20 | 01ea # TCP Window size 21 | fa02 # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 02 # TCP Option Maximum Segment Size 25 | 04 # TCP Option Length 26 | 01ea # TCP Maximum Segment Size 27 | -------------------------------------------------------------------------------- /tests/packets/http_response_plain_on.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0059 # Length (89 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 0050 # TCP Source port number (80) 14 | e899 # TCP Destination port number (59545) 15 | 16 | eabf6f22 # TCP Sequence number 17 | bb55a9fd # TCP Acknowledgement number 18 | 60 # TCP Header length (24 bytes) 19 | 19 # TCP Flags (ACK, FIN, PSH) 20 | 01ea # TCP Window size (490 bytes) 21 | 8ff4 # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 02 04 01ea # Maximum segment size: 490 bytes 25 | 26 | "HTTP/1.0 200 OK\r\n" 27 | "Server: EtherSia\r\n" 28 | "Content-Type: text/plain\r\n" 29 | "\r\n" 30 | "on" 31 | -------------------------------------------------------------------------------- /tests/packets/udp_dns_request.hext: -------------------------------------------------------------------------------- 1 | ca:2f:6d:70:f9:5f # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0029 # Length (41 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:4860:4860:0000:0000:0000:0000:8888 # IPv6 Destination Address 12 | 13 | 61a8 # UDP Source Port 14 | 0035 # UDP Destination Port 15 | 0029 # Length (41 bytes) 16 | 6d08 # Checksum 17 | 18 | 19 | 7fff # Request ID 20 | 0100 # Flags (Recursion Desired) 21 | 0001 # QD: Question Count 22 | 0000 # AN: Answer Count 23 | 0000 # NS: Name Server Count 24 | 0000 # AR: Additional Record Count 25 | 26 | # Response to question 27 | 04 "ipv6" 28 | 06 "aelius" 29 | 03 "com" 30 | 00 31 | 32 | 001c # Type 28 - IP6 Address 33 | 0001 # Class 1 - Internet 34 | -------------------------------------------------------------------------------- /tests/packets/http_response_not_found.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 006d # Length (109 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 0050 # TCP Source port number (80) 14 | e899 # TCP Destination port number (59545) 15 | 16 | eabf6f22 # TCP Sequence number 17 | bb55a9fd # TCP Acknowledgement number 18 | 60 # TCP Header length (24 bytes) 19 | 19 # TCP Flags (ACK, FIN, PSH) 20 | 01ea # TCP Window size (490 bytes) 21 | a4cd # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 02 04 01ea # Maximum segment size: 490 bytes 25 | 26 | "HTTP/1.0 404 Not Found\r\n" 27 | "Server: EtherSia\r\n" 28 | "Content-Type: text/plain\r\n" 29 | "\r\n" 30 | "404 Not Found\r\n" 31 | -------------------------------------------------------------------------------- /tests/packets/http_response_redirect_foo_bar.hext: -------------------------------------------------------------------------------- 1 | a4:5e:60:da:58:9d # Ethernet Destination 2 | 00:04:a3:2c:2b:b9 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0065 # Length (101 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 12 | 13 | 0050 # TCP Source port number (80) 14 | e899 # TCP Destination port number (59545) 15 | 16 | eabf6f22 # TCP Sequence number 17 | bb55a9fd # TCP Acknowledgement number 18 | 60 # TCP Header length (24 bytes) 19 | 19 # TCP Flags (ACK, FIN, PSH) 20 | 01ea # TCP Window size (490 bytes) 21 | c397 # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 02 04 01ea # Maximum segment size: 490 bytes 25 | 26 | "HTTP/1.0 302 Redirect\r\n" 27 | "Server: EtherSia\r\n" 28 | "Location: /foo/bar\r\n" 29 | "\r\n" 30 | "302 Redirect\r\n" 31 | -------------------------------------------------------------------------------- /tests/packets/tcp_receive_ack.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 0e 3a 19 # IPv6 header 6 | 0020 # Length (32 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | e899 # TCP Source port number (59545) 14 | 0050 # TCP Destination port number (80) 15 | 16 | bb55a98f # TCP Sequence number 17 | eabf6f22 # TCP Acknowledgement number 18 | 80 # TCP Header length (32 bytes) 19 | 10 # TCP Flags (ACK) 20 | 31c7 # TCP Window size 21 | d14b # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 01 # TCP Option NOP 25 | 01 # TCP Option NOP 26 | 08 # Timestamp option 27 | 0a # Timestamp length (10 bytes) 28 | 38706da2 # Timestamp value 29 | 38706da2 # Timestamp echo reply 30 | -------------------------------------------------------------------------------- /tests/packets/tcp_receive_fin_ack.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 07 48 b3 # IPv6 header 6 | 0020 # Length (32 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | cf0e # TCP Source port number (53006) 14 | 0050 # TCP Destination port number (80) 15 | 16 | 4932c803 # TCP Sequence number 17 | 62c3dcc7 # TCP Acknowledgement number 18 | 80 # TCP Header length (32 bytes) 19 | 11 # TCP Flags (FIN and ACK) 20 | 2995 # TCP Window size 21 | 65fc # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 01 # TCP Option NOP 25 | 01 # TCP Option NOP 26 | 08 # Timestamp option 27 | 0a # Timestamp length (10 bytes) 28 | 3602ed9b # Timestamp value 29 | 3602ed96 # Timestamp echo reply 30 | -------------------------------------------------------------------------------- /tests/packets/tcp_receive_data.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 0d 14 5e # IPv6 header 6 | 0032 # Length (50 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | e899 # TCP Source port number (59545) 14 | 0050 # TCP Destination port number (80) 15 | 16 | bb55a98f # TCP Sequence number 17 | eabf6f22 # TCP Acknowledgement number 18 | 80 # TCP Header length (32 bytes) 19 | 18 # TCP Flags (FIN and ACK) 20 | 31c7 # TCP Window size 21 | f291 # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 01 # TCP Option NOP 25 | 01 # TCP Option NOP 26 | 08 # Timestamp option 27 | 0a # Timestamp length (10 bytes) 28 | 38706da2 # Timestamp value 29 | 38706da2 # Timestamp echo reply 30 | 31 | "GET / HTTP/1.0\r\n\r\n" 32 | -------------------------------------------------------------------------------- /examples/Minimal/Minimal.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Minimal example - the minimum amount of code to start using EtherSia 3 | * 4 | * Doesn't do anything other than print the IP address and respond to pings. 5 | * 6 | * Uses a static MAC address, please update with your own. 7 | * 8 | * Get your own Random Locally Administered MAC Address here: 9 | * https://www.hellion.org.uk/cgi-bin/randmac.pl 10 | * 11 | * @file 12 | */ 13 | 14 | #include 15 | 16 | /** Ethernet Interface */ 17 | EtherSia_ENC28J60 ether; 18 | 19 | void setup() { 20 | MACAddress macAddress("3e:ad:93:36:7d:9d"); 21 | 22 | // Setup serial port for debugging 23 | Serial.begin(38400); 24 | Serial.println("[EtherSia Minimal]"); 25 | 26 | // Configure the Ethernet interface 27 | if (ether.begin(macAddress) == false) { 28 | Serial.println("Failed to configure Ethernet"); 29 | } 30 | 31 | Serial.print("Global address: "); 32 | ether.globalAddress().println(); 33 | } 34 | 35 | 36 | void loop() { 37 | ether.receivePacket(); 38 | 39 | // Do other stuff here 40 | // But avoid using delay() or anything that takes a long time 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Syslog.cpp: -------------------------------------------------------------------------------- 1 | #include "EtherSia.h" 2 | #include "util.h" 3 | 4 | Syslog::Syslog(EtherSia ðer) : UDPSocket(ether) 5 | { 6 | _pri = (LOG_LOCAL0 << 3) | LOG_INFO; 7 | } 8 | 9 | void Syslog::setPriority(uint8_t priority) 10 | { 11 | _pri = (_pri & 0xF8) + priority; 12 | } 13 | 14 | uint8_t Syslog::priority() 15 | { 16 | return _pri & 0x07; 17 | } 18 | 19 | void Syslog::setFacility(uint8_t facility) 20 | { 21 | _pri = (_pri & 0x07) + (facility << 3); 22 | } 23 | 24 | uint8_t Syslog::facility() 25 | { 26 | return (_pri & 0xF8) >> 3; 27 | } 28 | 29 | boolean Syslog::setRemoteAddress(const char *remoteAddress) 30 | { 31 | return UDPSocket::setRemoteAddress(remoteAddress, SysLogPortNumber); 32 | } 33 | 34 | boolean Syslog::handleWriteNewline() 35 | { 36 | uint8_t *packetBuffer = payload(); 37 | 38 | if (_writePos > 0) { 39 | packetBuffer[_writePos++] = '\0'; 40 | send((uint16_t)_writePos); 41 | _writePos = -1; 42 | } 43 | 44 | // Don't write the line break into the packet 45 | return false; 46 | } 47 | 48 | void Syslog::writePayloadHeader() 49 | { 50 | print('<'); 51 | print(_pri, DEC); 52 | print('>'); 53 | print(F("EtherSia: ")); 54 | } 55 | -------------------------------------------------------------------------------- /tests/libarduino/Arduino.h: -------------------------------------------------------------------------------- 1 | #ifndef Arduino_h 2 | #define Arduino_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define HIGH 0x1 9 | #define LOW 0x0 10 | 11 | #define INPUT 0x0 12 | #define OUTPUT 0x1 13 | #define INPUT_PULLUP 0x2 14 | 15 | extern "C" { 16 | typedef uint16_t word; 17 | typedef uint8_t byte ; 18 | typedef bool boolean ; 19 | 20 | void* malloc(size_t size); 21 | void free(void *ptr); 22 | } 23 | 24 | /* sketch */ 25 | extern void setup( void ) ; 26 | extern void loop( void ) ; 27 | 28 | uint32_t millis( void ); 29 | uint32_t micros( void ); 30 | void delay(uint32_t msec); 31 | void delayMicroseconds(uint32_t us); 32 | 33 | void pinMode(uint8_t, uint8_t); 34 | void digitalWrite(uint8_t, uint8_t); 35 | int digitalRead(uint8_t); 36 | 37 | long random(); 38 | long random(long); 39 | long random(long, long); 40 | void randomSeed(unsigned long); 41 | 42 | boolean isWhitespace(int c); 43 | 44 | #define lowByte(w) ((uint8_t) ((w) & 0xff)) 45 | #define highByte(w) ((uint8_t) ((w) >> 8)) 46 | #define word(b1, b2) ((((uint16_t)b1) << 8) | b2) 47 | 48 | #include "Progmem.h" 49 | 50 | #include "Print.h" 51 | #include "Stream.h" 52 | #include "Buffer.h" 53 | 54 | #endif // Arduino_h 55 | -------------------------------------------------------------------------------- /examples/WebToggler/eswt.css: -------------------------------------------------------------------------------- 1 | html, body 2 | { 3 | padding: 0; 4 | margin: 0; 5 | font-family: Helvetica, Arial, sans-serif; 6 | font-size: 14pt; 7 | background: #fff; 8 | } 9 | 10 | h1 11 | { 12 | font-size: 28pt; 13 | background-color: #DDDEE6; 14 | padding: 10px; 15 | margin: 0 0 10px 0; 16 | border-bottom: 2px #888 solid; 17 | } 18 | 19 | table, tr { 20 | width: 100% 21 | 22 | } 23 | 24 | td, th { 25 | margin: 0px; 26 | padding: 10px; 27 | } 28 | 29 | td { text-align: center; } 30 | 31 | th { text-align: right; } 32 | 33 | .on { background-color: #6F6; } 34 | .off { background-color: #F00; } 35 | 36 | button 37 | { 38 | box-shadow: inset 0px 1px 0px 0px #54a3f7; 39 | background: linear-gradient(to bottom, #007dc1 5%, #0061a7 100%); 40 | background-color: #007dc1; 41 | border-radius: 3px; 42 | border: 1px solid #124d77; 43 | display: block; 44 | cursor: pointer; 45 | color: #ffffff; 46 | font-size: 13px; 47 | padding: 6px 24px; 48 | text-decoration: none; 49 | text-shadow: 0px 1px 0px #154682; 50 | } 51 | 52 | button:hover { 53 | background:linear-gradient(to bottom, #0061a7 5%, #007dc1 100%); 54 | background-color:#0061a7; 55 | } 56 | 57 | button:active { 58 | position:relative; 59 | top:1px; 60 | } 61 | -------------------------------------------------------------------------------- /tests/packets/tcp_receive_syn.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 01 cb 2f # IPv6 header 6 | 002c # Length (44 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | cfed # TCP Source port number (53229) 14 | 0050 # TCP Destination port number (80) 15 | 16 | 6fbb7776 # TCP Sequence number 17 | 00000000 # TCP Acknowledgement number 18 | b0 # TCP Header length (44 bytes) 19 | 02 # TCP Flags (SYN) 20 | ffff # TCP Window size 21 | 8d5a # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 02 04 05 a0 # Maximum segment size: 1440 bytes 25 | 01 # NOP 26 | 03 03 05 # Window scale: 5 27 | 01 # NOP 28 | 01 # NOP 29 | 08 0a # Time Stamp Option (8) 30 | 37 d9 3a 9a # Timestamp value: 936983194 31 | 00 00 00 00 # Timestamp echo reply: 0 32 | 04 02 # TCP SACK permitted: True 33 | 00 00 # End of Option List 34 | -------------------------------------------------------------------------------- /tests/packets/http_get_root.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 0d 14 5e # IPv6 header 6 | 008e # Length (142 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | e899 # TCP Source port number (59545) 14 | 0050 # TCP Destination port number (80) 15 | 16 | bb55a98f # TCP Sequence number 17 | eabf6f22 # TCP Acknowledgement number 18 | 80 # TCP Header length (32 bytes) 19 | 18 # TCP Flags (ACK,PSH) 20 | 31c7 # TCP Window size 21 | 7624 # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 01 # TCP Option NOP 25 | 01 # TCP Option NOP 26 | 08 # Timestamp option 27 | 0a # Timestamp length (10 bytes) 28 | 38706da2 # Timestamp value 29 | 38706da2 # Timestamp echo reply 30 | 31 | "GET / HTTP/1.1\r\n" 32 | "Host: [2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9]:3000\r\n" 33 | "User-Agent: curl/7.44.0\r\n" 34 | "Accept: */*\r\n\r\n" 35 | -------------------------------------------------------------------------------- /tests/10_check_endian.tc: -------------------------------------------------------------------------------- 1 | 2 | // Ensure we use the implementations in esendian.h when testing 3 | #ifdef htons 4 | #undef htons 5 | #endif 6 | #ifdef ntohs 7 | #undef ntohs 8 | #endif 9 | #ifdef htonl 10 | #undef htonl 11 | #endif 12 | #ifdef ntohl 13 | #undef ntohl 14 | #endif 15 | 16 | #include "esendian.h" 17 | #suite Endian 18 | 19 | 20 | // NOTE: these tests assume that the host running the tests is little-endian 21 | 22 | #test htons_0x1234 23 | ck_assert(htons(0x1234) == 0x3412); 24 | 25 | #test ntohs_0x1234 26 | ck_assert(ntohs(0x1234) == 0x3412); 27 | 28 | #test htons_0xFF00 29 | ck_assert(htons(0xFF00) == 0x00FF); 30 | 31 | #test htons_0xFFFF 32 | ck_assert(htons(0xFFFF) == 0xFFFF); 33 | 34 | 35 | 36 | #test htonl_0x12345678 37 | ck_assert(htonl(0x12345678) == 0x78563412); 38 | 39 | #test ntohl_0x12345678 40 | ck_assert(ntohl(0x12345678) == 0x78563412); 41 | 42 | #test htonl_0xFF000000 43 | ck_assert(htonl(0xFF000000) == 0x000000FF); 44 | 45 | #test htonl_0xFFFFFFFF 46 | ck_assert(htonl(0xFFFFFFFF) == 0xFFFFFFFF); 47 | 48 | 49 | 50 | #test bytesToWord_0x00_0x00 51 | ck_assert(bytesToWord(0x00, 0x00) == 0x0000); 52 | 53 | #test bytesToWord_0x12_0x34 54 | ck_assert(bytesToWord(0x12, 0x34) == 0x1234); 55 | 56 | #test bytesToWord_0xFF_0xFF 57 | ck_assert(bytesToWord(0xFF, 0xFF) == 0xFFFF); 58 | -------------------------------------------------------------------------------- /tests/packets/http_post_output1_off.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | a4:5e:60:da:58:9d # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 0d 14 5e # IPv6 header 6 | 00b9 # Length (142 bytes) 7 | 06 # Protocol (TCP) 8 | 40 # Hop Limit 9 | 10 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | e899 # TCP Source port number (59545) 14 | 0050 # TCP Destination port number (80) 15 | 16 | bb55a98f # TCP Sequence number 17 | eabf6f22 # TCP Acknowledgement number 18 | 80 # TCP Header length (32 bytes) 19 | 18 # TCP Flags (ACK,PSH) 20 | 31c7 # TCP Window size 21 | 2714 # TCP Checksum 22 | 0000 # TCP Urgent Pointer 23 | 24 | 01 # TCP Option NOP 25 | 01 # TCP Option NOP 26 | 08 # Timestamp option 27 | 0a # Timestamp length (10 bytes) 28 | 38706da2 # Timestamp value 29 | 38706da2 # Timestamp echo reply 30 | 31 | "POST /output1 HTTP/1.1\r\n" 32 | "Host: [::1]:3000\r\n" 33 | "User-Agent: curl/7.44.0\r\n" 34 | "Accept: */*\r\n" 35 | "Content-Length: 3\r\n" 36 | "Content-Type: application/x-www-form-urlencoded\r\n" 37 | "\r\n" 38 | "off" 39 | -------------------------------------------------------------------------------- /tests/packets/udp_dns_response.hext: -------------------------------------------------------------------------------- 1 | 00:04:a3:2c:2b:b9 # Ethernet Destination 2 | ca:2f:6d:70:f9:5f # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0045 # Length (69 bytes) 7 | 11 # Protocol 8 | 40 # Hop Limit 9 | 10 | 2001:4860:4860:0000:0000:0000:0000:8888 # IPv6 Source Address 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 12 | 13 | 0035 # UDP Source Port 14 | 61a8 # UDP Destination Port 15 | 0045 # Length (69 bytes) 16 | 5221 # Checksum 17 | 18 | 19 | 7fff # Request ID 20 | 8180 # Flags 21 | 0001 # QD: Question Count 22 | 0001 # AN: Answer Count 23 | 0000 # NS: Name Server Count 24 | 0000 # AR: Additional Record Count 25 | 26 | # Question Section 27 | 04 "ipv6" 28 | 06 "aelius" 29 | 03 "com" 30 | 00 31 | 32 | 001c # Type 28 - IP6 Address 33 | 0001 # Class 1 - Internet 34 | 35 | # Answer Section 36 | c0 0c # Pointer to name in the question section 37 | 38 | 001c # Type 28 - IP6 Address 39 | 0001 # Class 1 - Internet 40 | 00000371 # Time to Live 41 | 0010 # Record Length (16 bytes) 42 | 43 | 2001:41c8:0051:07cf:0000:0000:0000:0006 44 | 45 | -------------------------------------------------------------------------------- /examples/Syslog/Syslog.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Example of sending Syslog messages from EtherSia 3 | * 4 | * You can print log lines to the syslog object. 5 | * Log lines will be sent upon writing a newline character (eg using println). 6 | * 7 | * 8 | * This example uses a static MAC address, please update with your own. 9 | * 10 | * Get your own Random Locally Administered MAC Address here: 11 | * https://www.hellion.org.uk/cgi-bin/randmac.pl 12 | * 13 | * @file 14 | */ 15 | 16 | #include 17 | 18 | /** Ethernet Interface (with Chip Select connected to Pin 10) */ 19 | EtherSia_W5100 ether; 20 | 21 | /** Define the syslog UDP socket object */ 22 | Syslog syslog(ether); 23 | 24 | 25 | void setup() { 26 | MACAddress macAddress("0a:2c:8c:ba:66:2d"); 27 | 28 | // Start Ethernet 29 | ether.begin(macAddress); 30 | 31 | // Set the address of the syslog server 32 | syslog.setRemoteAddress("logger.example.com"); 33 | } 34 | 35 | 36 | void loop() { 37 | ether.receivePacket(); 38 | 39 | static unsigned long nextMessage = millis(); 40 | if ((long)(millis() - nextMessage) >= 0) { 41 | syslog.print("Seconds since boot="); 42 | syslog.println(millis() / 1000, DEC); 43 | nextMessage = millis() + 5000; 44 | } 45 | 46 | // Reject any incoming connections 47 | ether.rejectPacket(); 48 | } 49 | -------------------------------------------------------------------------------- /tests/packets/icmp6_unrecognized_next_header.hext: -------------------------------------------------------------------------------- 1 | ## This is a response to ipv6_unknown_protcol.hext 2 | a4:5e:60:da:58:9d # Ethernet Destination 3 | 00:04:a3:2c:2b:b9 # Ethernet Source 4 | 86dd # EtherType (IPv6) 5 | 6 | 60 00 00 00 # IPv6 header 7 | 003d # Length (13 bytes) 8 | 3a # ICMPv6 Protocol 9 | 40 # Hop Limit 10 | 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 12 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 13 | 14 | 04 # ICMPv6 Type - Parameter Problem 15 | 01 # ICMPv6 Code - Unrecognised Next Header type encountered 16 | 1f69 # Checksum 17 | 18 | 00 00 00 06 # Pointer to the Unrecognised Next Header 19 | 20 | 21 | 22 | 23 | ## The original packet 24 | 25 | 60 00 00 00 # IPv6 header 26 | 00 0d # Length (16 bytes) 27 | fd # Protocol - Use for experimentation and testing (RFC3692) 28 | 40 # Hop Limit 29 | 30 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 31 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 32 | 33 | 34 | # Our made-up protocol 35 | 2d53 # Checksum 36 | "Hello World" 37 | -------------------------------------------------------------------------------- /tests/packets/icmp6_port_unreachable.hext: -------------------------------------------------------------------------------- 1 | ## This is a response to udp_valid_hello.hext 2 | a4:5e:60:da:58:9d # Ethernet Destination 3 | 00:04:a3:2c:2b:b9 # Ethernet Source 4 | 86dd # EtherType (IPv6) 5 | 6 | 60 00 00 00 # IPv6 header 7 | 003d # Length (13 bytes) 8 | 3a # ICMPv6 Protocol 9 | 40 # Hop Limit 10 | 11 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Source Address 12 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Destination Address 13 | 14 | 01 # ICMPv6 Type - Destination Unreachable 15 | 04 # ICMPv6 Code - Port Unreachable 16 | 5bc6 # Checksum 17 | 18 | 00 00 00 00 # Unused 19 | 20 | 21 | ## The original packet 22 | 23 | 60 03 b1 b7 # IPv6 header 24 | 000d # Length (13 bytes) 25 | 11 # Protocol 26 | 40 # Hop Limit 27 | 28 | 2001:08b0:ffd5:0003:a65e:60ff:feda:589d # IPv6 Source Address 29 | 2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9 # IPv6 Destination Address 30 | 31 | fa06 # UDP Source Port 32 | 03f0 # UDP Destination Port 33 | 000d # Length (32 bytes) 34 | 5e37 # Checksum 35 | "Hello" # UDP Payload 36 | -------------------------------------------------------------------------------- /tests/libarduino/SPI.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef ARDUINO_SPI 4 | #define ARDUINO_SPI 1 5 | 6 | #ifndef LSBFIRST 7 | #define LSBFIRST 0 8 | #endif 9 | #ifndef MSBFIRST 10 | #define MSBFIRST 1 11 | #endif 12 | 13 | #define SPI_CLOCK_DIV4 0x00 14 | #define SPI_CLOCK_DIV16 0x01 15 | #define SPI_CLOCK_DIV64 0x02 16 | #define SPI_CLOCK_DIV128 0x03 17 | #define SPI_CLOCK_DIV2 0x04 18 | #define SPI_CLOCK_DIV8 0x05 19 | #define SPI_CLOCK_DIV32 0x06 20 | 21 | #define SPI_MODE0 0x00 22 | #define SPI_MODE1 0x04 23 | #define SPI_MODE2 0x08 24 | #define SPI_MODE3 0x0C 25 | 26 | #define SPI_MODE_MASK 0x0C 27 | #define SPI_CLOCK_MASK 0x03 28 | #define SPI_2XCLOCK_MASK 0x01 29 | 30 | static const uint8_t SS = 10; 31 | static const uint8_t MOSI = 11; 32 | static const uint8_t MISO = 12; 33 | static const uint8_t SCK = 13; 34 | 35 | class SPISettings { 36 | public: 37 | SPISettings(uint32_t /*clock*/, uint8_t /*bitOrder*/, uint8_t /*dataMode*/) { 38 | } 39 | }; 40 | 41 | class SPIClass { 42 | public: 43 | static void begin(); 44 | 45 | static uint8_t transfer(uint8_t val); 46 | 47 | static void end(); 48 | 49 | static void beginTransaction(SPISettings settings); 50 | static void endTransaction(); 51 | 52 | static void setBitOrder(uint8_t bitOrder); 53 | static void setDataMode(uint8_t dataMode); 54 | static void setClockDivider(uint8_t clockDiv); 55 | }; 56 | 57 | extern SPIClass SPI; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /examples/MinimalStatic/MinimalStatic.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Minimal static example - example of disabling stateless autoconfiguration 3 | * 4 | * Doesn't do anything other than configure a static IP address and respond to pings. 5 | * 6 | * Uses a static MAC address, please update with your own. 7 | * 8 | * Get your own Random Locally Administered MAC Address here: 9 | * https://www.hellion.org.uk/cgi-bin/randmac.pl 10 | * 11 | * @file 12 | */ 13 | 14 | #include 15 | 16 | /** Ethernet Interface */ 17 | EtherSia_ENC28J60 ether; 18 | 19 | void setup() { 20 | MACAddress macAddress("e6:a6:57:21:ec:d1"); 21 | 22 | // Setup serial port for debugging 23 | Serial.begin(57600); 24 | Serial.println("[EtherSia MinimalStatic]"); 25 | 26 | ether.disableAutoconfiguration(); 27 | 28 | // Start the Ethernet interface 29 | if (ether.begin(macAddress) == false) { 30 | Serial.println("Failed to configure Ethernet"); 31 | } 32 | 33 | // Configure a static global address and router addresses 34 | ether.setGlobalAddress("2001:1234::5000"); 35 | if (ether.setRouter("fe80::f4c0:4ff:fefb:4186") == false) { 36 | Serial.println("Failed to configure router address"); 37 | } 38 | 39 | Serial.println("Ready"); 40 | } 41 | 42 | 43 | void loop() { 44 | ether.receivePacket(); 45 | 46 | // Do other stuff here 47 | // But avoid using delay() or anything that takes a long time 48 | 49 | } 50 | -------------------------------------------------------------------------------- /tests/ipv6checksum.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Small tool to calculate the checksum of an IPv6 packet in a Hext file 4 | 5 | */ 6 | 7 | #include "EtherSia.h" 8 | #include "hext.hh" 9 | 10 | #include 11 | 12 | int main(int argc, char** argv) 13 | { 14 | if (argc < 2) { 15 | fprintf(stderr, "Usage: ipv6_checksum \n"); 16 | return -1; 17 | } 18 | 19 | HextFile input(argv[1]); 20 | IPv6Packet *packet = (IPv6Packet *)input.buffer; 21 | 22 | printf(" Input filename: %s\n", argv[1]); 23 | printf(" Input file length: %d\n", input.length); 24 | 25 | // Check the EtherType 26 | if (packet->etherType() != ETHER_TYPE_IPV6) { 27 | fprintf(stderr, "Error: Packet's EtherType is not 0x%4.4x\n", ETHER_TYPE_IPV6); 28 | return -1; 29 | } 30 | 31 | // Check the version header 32 | if (packet->version() != 6) { 33 | fprintf(stderr, "Error: Packet's IP version is not 6\n"); 34 | return -1; 35 | } 36 | 37 | printf(" Packet length: %d\n", packet->length()); 38 | printf(" Payload length: %d\n", packet->payloadLength()); 39 | if (packet->length() != input.length) { 40 | fprintf(stderr, "Error: packet length != file length\n"); 41 | fprintf(stderr, "Payload length should = 0x%4.4x\n", input.length - ETHER_HEADER_LEN - IP6_HEADER_LEN); 42 | return -1; 43 | } 44 | 45 | printf(" Packet Checksum: 0x%4.4x\n", packet->calculateChecksum()); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /tests/packets/dns_res_aelius.hext: -------------------------------------------------------------------------------- 1 | # Response for AAAA ipv6.aelius.com 2 | 3 | 1234 # Request ID 4 | 8180 # Flags 5 | 0001 # QD: Question Count 6 | 0001 # AN: Answer Count 7 | 0003 # NS: Name Server Count 8 | 0000 # AR: Additional Record Count 9 | 10 | # Question Section 11 | 04 "ipv6" 12 | 06 "aelius" 13 | 03 "com" 14 | 00 15 | 16 | 001c # Type 28 - IP6 Address 17 | 0001 # Class 1 - Internet 18 | 19 | # Answer Section 20 | c0 0c # Pointer to name in the question section 21 | 22 | 001c # Type 28 - IP6 Address 23 | 0001 # Class 1 - Internet 24 | 00000371 # Time to Live 25 | 0010 # Record Length (16 bytes) 26 | 27 | 2001:41c8:0051:07cf:0000:0000:0000:0006 28 | 29 | # Name Server Record 30 | c011 # Pointer to Question 31 | 32 | 0002 # An authoritative name server 33 | 0001 # Class 1 - Internet 34 | 00000370 # Time to Live 35 | 0015 # Record Length (21 bytes) 36 | 37 | 01 "a" 38 | 02 "ns" 39 | 08 "bytemark" 40 | 02 "co" 41 | 02 "uk" 42 | 00 43 | 44 | # Name Server Record 45 | c011 # Pointer to Question 46 | 0002 # An authoritative name server 47 | 0001 # Class 1 - Internet 48 | 00000370 # Time to Live 49 | 0004 # Record Length (4 bytes) 50 | 51 | 01 "b" 52 | c0 4b # Pointer to "ns.bytemark.co.uk" 53 | 54 | 55 | # Name Server Record 56 | c011 # Pointer to Question 57 | 0002 # An authoritative name server 58 | 0001 # Class 1 - Internet 59 | 00000370 # Time to Live 60 | 0004 # Record Length (4 bytes) 61 | 62 | 01 "c" 63 | c0 4b # Pointer to "ns.bytemark.co.uk" 64 | -------------------------------------------------------------------------------- /tests/packets/icmp6_router_advertisment.hext: -------------------------------------------------------------------------------- 1 | 33:33:00:00:00:01 # Ethernet Destination 2 | 3c:61:04:d4:8d:88 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0038 # Length 7 | 3a # ICMPv6 Protocol 8 | ff # Hop Limit 9 | 10 | fe80:0000:0000:0000:3e61:04ff:fed4:8d88 # IPv6 Source Address 11 | ff02:0000:0000:0000:0000:0000:0000:0001 # IPv6 Destination Address 12 | 13 | 86 # ICMPv6 router advertisement (134) 14 | 00 # ICMPv6 Code 15 | 6508 # Checksum 16 | 17 | 40 # ICMPv6 RA: Current Hop Limit 18 | 00 # ICMPv6 RA: Flags 19 | 0384 # ICMPv6 RA: Router Lifetime 20 | 00000000 # ICMPv6 RA: Reachable Time 21 | 00000000 # ICMPv6 RA: Re-trans Timer 22 | 23 | 01 # ICMPv6 Option 1: Source Link-Layer Address 24 | 01 # ICMPv6 Option Length: 8 bytes 25 | 3c:61:04:d4:8d:88 # Router MAC 26 | 27 | 03 # ICMPv6 Option 3: Prefix Information 28 | 04 # ICMPv6 Option Length: 32 bytes 29 | 30 | 40 # ICMPv6 Prefix: Length 31 | c0 # ICMPv6 Prefix: Flags (On-link flag & Autoconf Flag) 32 | 00278d00 # ICMPv6 Prefix: Valid Lifetime (30 days) 33 | 00093a80 # ICMPv6 Prefix: Preferred Lifetime (7 days) 34 | 00000000 # ICMPv6 Prefix: Reserved 35 | 36 | 2001:08b0:ffd5:0003:0000:0000:0000:0000 # IPv6 prefix 37 | -------------------------------------------------------------------------------- /examples/PacketPrinter/PacketPrinter.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Packet Printer - prints Ethernet and IPv6 packet headers received to Serial 3 | * 4 | * Uses a static MAC address, please update with your own. 5 | * 6 | * Get your own Random Locally Administered MAC Address here: 7 | * https://www.hellion.org.uk/cgi-bin/randmac.pl 8 | * 9 | * @file 10 | */ 11 | 12 | #include 13 | 14 | /** Ethernet Interface with ENC28J60 (with Chip Select connected to Pin 10) */ 15 | EtherSia_ENC28J60 ether(10); 16 | 17 | void setup() { 18 | MACAddress macAddress("e2:d7:66:39:6b:5e"); 19 | 20 | // Setup serial port for debugging 21 | Serial.begin(38400); 22 | Serial.println("[EtherSia PacketPrinter]"); 23 | 24 | Serial.print("Our MAC is: "); 25 | macAddress.println(); 26 | 27 | if (ether.begin(macAddress) == false) { 28 | Serial.println("Failed to configure Ethernet"); 29 | } 30 | } 31 | 32 | 33 | void loop() { 34 | if (ether.receivePacket()) { 35 | IPv6Packet& packet = ether.packet(); 36 | 37 | Serial.print("Source MAC: "); 38 | packet.etherSource().println(); 39 | 40 | Serial.print("Destination MAC: "); 41 | packet.etherDestination().println(); 42 | 43 | Serial.print("Source Address: "); 44 | packet.source().println(); 45 | 46 | Serial.print("Destination Address: "); 47 | packet.destination().println(); 48 | 49 | Serial.print("Protocol: "); 50 | Serial.println(packet.protocol(), DEC); 51 | 52 | Serial.print("Length: "); 53 | Serial.println(packet.payloadLength(), DEC); 54 | 55 | Serial.println(); 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /tests/libarduino/Print.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | size_t Print::print(const char str[]) 5 | { 6 | unsigned int len = strlen(str); 7 | for(unsigned int i=0; i(ifsh); 16 | return print(str); 17 | } 18 | 19 | size_t Print::print(char c) 20 | { 21 | return write(c); 22 | } 23 | 24 | size_t Print::print(int i, int base) 25 | { 26 | char buf[12] = "\0"; 27 | if (base == DEC) { 28 | snprintf(buf, sizeof(buf), "%d", i); 29 | } else if (base == HEX) { 30 | snprintf(buf, sizeof(buf), "%x", i); 31 | } 32 | return print(buf); 33 | } 34 | 35 | size_t Print::print(unsigned int i, int base) 36 | { 37 | char buf[12] = "\0"; 38 | if (base == DEC) { 39 | snprintf(buf, sizeof(buf), "%u", i); 40 | } else if (base == HEX) { 41 | snprintf(buf, sizeof(buf), "%x", i); 42 | } 43 | return print(buf); 44 | } 45 | 46 | size_t Print::println(const char str[]) 47 | { 48 | return print(str) + println(); 49 | } 50 | 51 | size_t Print::println(const __FlashStringHelper* ifsh) 52 | { 53 | return print(ifsh) + println(); 54 | } 55 | 56 | size_t Print::println(char c) 57 | { 58 | return print(c) + println(); 59 | } 60 | 61 | size_t Print::println(int i, int base) 62 | { 63 | return print(i, base) + println(); 64 | } 65 | 66 | size_t Print::println(unsigned int i, int base) 67 | { 68 | return print(i, base) + println(); 69 | } 70 | 71 | size_t Print::println(void) 72 | { 73 | return print("\r\n"); 74 | } 75 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -Werror -Wall -Wextra -pedantic -g -O0 2 | CFLAGS += -I../src -I./libarduino 3 | CFLAGS += $(COVERAGE_CFLAGS) 4 | CXXFLAGS += -std=c++11 5 | 6 | # FIXME: work out how to apply this to the clang compiler only 7 | CFLAGS += -Wno-gnu-zero-variadic-macro-arguments 8 | 9 | 10 | CHECK_CFLAGS = $(shell pkg-config --cflags check) 11 | CHECK_LIBS = $(shell pkg-config --libs check) 12 | 13 | LIBARDUINO_SOURCES=$(wildcard libarduino/*.cpp) 14 | LIBARDUINO_OBJECTS=$(LIBARDUINO_SOURCES:%.cpp=%.o) 15 | 16 | LIBETHERSIA_SOURCES=$(wildcard ../src/*.cpp) 17 | LIBETHERSIA_OBJECTS=$(LIBETHERSIA_SOURCES:%.cpp=%.o) 18 | 19 | TEST_SUITES=$(wildcard *.tc) 20 | TEST_SOURCES=$(TEST_SUITES:%.tc=%.cpp) 21 | TEST_COMMANDS=$(TEST_SUITES:%.tc=%.cmd) 22 | 23 | 24 | 25 | test: check 26 | 27 | check: $(TEST_COMMANDS) 28 | @$(foreach f,$^,./$(f) &&) echo "All tests passed." 29 | 30 | %.o: %.cpp 31 | $(CXX) $(CXXFLAGS) $(CFLAGS) $(CHECK_CFLAGS) -c -o $@ $< 32 | 33 | libarduino.a: $(LIBARDUINO_OBJECTS) 34 | $(AR) rcs $@ $^ 35 | 36 | libethersia.a: $(LIBETHERSIA_OBJECTS) 37 | $(AR) rcs $@ $^ 38 | 39 | libhext.a: hext.o 40 | $(AR) rcs $@ $^ 41 | 42 | %.cpp: %.tc 43 | checkmk $< > $@ || rm -f $@ 44 | 45 | %.cmd: %.o libarduino.a libethersia.a libhext.a 46 | $(CXX) -o $@ $< -L. -lethersia -larduino -lhext $(CHECK_LIBS) $(CFLAGS) 47 | 48 | ipv6checksum: ipv6checksum.cpp libarduino.a libethersia.a libhext.a 49 | $(CXX) -o $@ $< -L. -lethersia -larduino -lhext $(CXXFLAGS) $(CFLAGS) 50 | 51 | 52 | clean: 53 | rm -f libarduino.a $(LIBARDUINO_OBJECTS) 54 | rm -f libethersia.a $(LIBETHERSIA_OBJECTS) 55 | rm -f libhext.a 56 | rm -f ipv6checksum 57 | rm -f $(TEST_SOURCES) *.o *.cmd 58 | 59 | .PHONY: test check clean 60 | -------------------------------------------------------------------------------- /tests/packets/dns_res_long.hext: -------------------------------------------------------------------------------- 1 | # Long Response for AAAA www.apple.com 2 | # Includes 2x CNAME, 2x AAAA and Authoritative Name servers 3 | bb 91 81 80 00 01 00 05 00 09 00 04 03 77 77 77 05 61 4 | 70 70 6c 65 03 63 6f 6d 00 00 1c 00 01 c0 0c 00 05 00 5 | 01 00 00 00 fd 00 1b 03 77 77 77 05 61 70 70 6c 65 03 6 | 63 6f 6d 07 65 64 67 65 6b 65 79 03 6e 65 74 00 c0 2b 7 | 00 05 00 01 00 00 31 5c 00 2f 03 77 77 77 05 61 70 70 8 | 6c 65 03 63 6f 6d 07 65 64 67 65 6b 65 79 03 6e 65 74 9 | 0b 67 6c 6f 62 61 6c 72 65 64 69 72 06 61 6b 61 64 6e 10 | 73 c0 41 c0 52 00 05 00 01 00 00 06 66 00 18 05 65 36 11 | 38 35 38 04 64 73 63 63 0a 61 6b 61 6d 61 69 65 64 67 12 | 65 c0 41 c0 8d 00 1c 00 01 00 00 00 09 00 10 2a 02 26 13 | f0 00 5d 02 a8 00 00 00 00 00 00 1a ca c0 8d 00 1c 00 14 | 01 00 00 00 09 00 10 2a 02 26 f0 00 5d 02 a5 00 00 00 15 | 00 00 00 1a ca c0 93 00 02 00 01 00 00 03 b5 00 09 06 16 | 6e 31 64 73 63 63 c0 98 c0 93 00 02 00 01 00 00 03 b5 17 | 00 09 06 6e 30 64 73 63 63 c0 98 c0 93 00 02 00 01 00 18 | 00 03 b5 00 09 06 6e 32 64 73 63 63 c0 98 c0 93 00 02 19 | 00 01 00 00 03 b5 00 09 06 6e 34 64 73 63 63 c0 98 c0 20 | 93 00 02 00 01 00 00 03 b5 00 09 06 61 30 64 73 63 63 21 | c0 98 c0 93 00 02 00 01 00 00 03 b5 00 09 06 6e 35 64 22 | 73 63 63 c0 98 c0 93 00 02 00 01 00 00 03 b5 00 09 06 23 | 6e 36 64 73 63 63 c0 98 c0 93 00 02 00 01 00 00 03 b5 24 | 00 09 06 61 31 64 73 63 63 c0 98 c0 93 00 02 00 01 00 25 | 00 03 b5 00 09 06 6e 33 64 73 63 63 c0 98 c1 3d 00 1c 26 | 00 01 00 00 13 55 00 10 2a 02 26 f0 00 7d f0 00 2b 8a 27 | 40 50 94 6a bb 97 c1 7c 00 1c 00 01 00 00 13 55 00 10 28 | 26 00 14 80 e8 00 00 00 00 00 00 00 00 00 00 c0 c0 fe 29 | 00 01 00 01 00 00 04 b7 00 04 17 3d fb aa c0 e9 00 01 30 | 00 01 00 00 08 97 00 04 5c 7b 4e 37 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Modified BSD License 2 | ==================== 3 | 4 | EtherSia contains code from the [Contiki] project, which is distributed under 5 | the same license as this project. 6 | 7 | _Copyright (c) 2016, Nicholas Humfrey_ 8 | _Copyright (c) 2012-2013, Thingsquare_ 9 | _Copyright (c) 2001-2003, Adam Dunkels_ 10 | _All rights reserved._ 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | 1. Redistributions of source code must retain the above copyright 16 | notice, this list of conditions and the following disclaimer. 17 | 2. Redistributions in binary form must reproduce the above copyright 18 | notice, this list of conditions and the following disclaimer in the 19 | documentation and/or other materials provided with the distribution. 20 | 3. Neither the name of the copyright holder nor the 21 | names of its contributors may be used to endorse or promote products 22 | derived from this software without specific prior written permission. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND 25 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 28 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 31 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | 36 | [Contiki]: http://www.contiki-os.org/ 37 | -------------------------------------------------------------------------------- /examples/MiniHTTPServer/MiniHTTPServer.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Mini HTTP Server - demonstrates handling HTTP requests 3 | * 4 | * This example handles two endpoints: 5 | * / - Displays "Hello World" as HTML 6 | * /text - Displays some plain text 7 | * 8 | * All other requests will display "404 Not Found". 9 | * 10 | * Get your own Random Locally Administered MAC Address here: 11 | * https://www.hellion.org.uk/cgi-bin/randmac.pl 12 | * 13 | * @file 14 | */ 15 | 16 | #include 17 | 18 | /** W5100 Ethernet Interface */ 19 | EtherSia_W5100 ether; 20 | 21 | /** Define HTTP server */ 22 | HTTPServer http(ether); 23 | 24 | 25 | void setup() { 26 | MACAddress macAddress("9e:b3:19:c7:1b:10"); 27 | 28 | // Setup serial port 29 | Serial.begin(115200); 30 | Serial.println("[EtherSia MiniHTTPServer]"); 31 | macAddress.println(); 32 | 33 | // Start Ethernet 34 | if (ether.begin(macAddress) == false) { 35 | Serial.println("Failed to configure Ethernet"); 36 | } 37 | 38 | Serial.print("Our link-local address is: "); 39 | ether.linkLocalAddress().println(); 40 | Serial.print("Our global address is: "); 41 | ether.globalAddress().println(); 42 | 43 | Serial.println("Ready."); 44 | } 45 | 46 | void loop() { 47 | ether.receivePacket(); 48 | 49 | if (http.isGet(F("/"))) { 50 | http.printHeaders(http.typeHtml); 51 | http.println(F("

Hello World

")); 52 | http.sendReply(); 53 | 54 | } else if (http.isGet(F("/text"))) { 55 | http.printHeaders(http.typePlain); 56 | http.println(F("This is some plain text")); 57 | http.sendReply(); 58 | 59 | } else if (http.havePacket()) { 60 | // Some other HTTP request, return 404 61 | http.notFound(); 62 | 63 | } else { 64 | // Some other packet, reply with rejection 65 | ether.rejectPacket(); 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/TFTPServer/TFTPServer.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Minimal TFTP Server example (RFC 1350) 3 | * 4 | * This sketch allows you to: 5 | * - write a file to the serial port (using the 'serial' filename) 6 | * - read/write the internal EEPROM (using the 'eeprom' filename) 7 | * - read/write an external AT24C128 EEPROM (using the 'i2c' filename) 8 | * 9 | * On your computer open a TFTP tool and connect to the Arduino. 10 | * Switch to 'binary' mode and then upload a local filename to the 'serial' 11 | * remote filename. 12 | * 13 | * tftp 2a00:1098:8:68:6084:98ff:fe22:092c 14 | * tftp> binary 15 | * tftp> put README.md serial 16 | * 17 | * 18 | * This example uses a static MAC address, please update with your own. 19 | * 20 | * Get your own Random Locally Administered MAC Address here: 21 | * https://www.hellion.org.uk/cgi-bin/randmac.pl 22 | * 23 | * @file 24 | */ 25 | 26 | #include 27 | #include "CustomTFTPServer.h" 28 | 29 | 30 | /** Ethernet Interface (with Chip Select connected to Pin 10) */ 31 | EtherSia_ENC28J60 ether(10); 32 | 33 | /** Instance of our custom TFTP server */ 34 | CustomTFTPServer tftp(ether); 35 | 36 | void setup() { 37 | MACAddress macAddress("62:84:98:22:09:2c"); 38 | 39 | // Setup serial port 40 | Serial.begin(57600); 41 | Serial.println("[EtherSia TFTPServer]"); 42 | 43 | // Setup i2c 44 | Wire.begin(); 45 | 46 | // Start Ethernet 47 | if (ether.begin(macAddress) == false) { 48 | Serial.println("Failed to configure Ethernet"); 49 | } 50 | 51 | Serial.print("Our link-local address is: "); 52 | ether.linkLocalAddress().println(); 53 | Serial.print("Our global address is: "); 54 | ether.globalAddress().println(); 55 | 56 | Serial.println("Ready."); 57 | } 58 | 59 | 60 | void loop() { 61 | ether.receivePacket(); 62 | 63 | if (!tftp.handleRequest()) { 64 | // If TFTP didn't handle the request, reject the packet 65 | ether.rejectPacket(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/LinuxSocket.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header file for using EtherSia with a network Socket on Linux 3 | * @file LinuxSocket.h 4 | */ 5 | 6 | #ifndef LINUXSOCKET_H 7 | #define LINUXSOCKET_H 8 | 9 | #include 10 | 11 | #include "EtherSia.h" 12 | 13 | /** 14 | * Run EtherSia on Linux using a raw socket to Send and receive Ethernet frames 15 | * Not intended for use with running EtherSia on Arduino. 16 | * 17 | * @note this is probably only useful for testing and development of EtherSia. 18 | */ 19 | class EtherSia_LinuxSocket : public EtherSia { 20 | 21 | public: 22 | /** 23 | * Constructor 24 | * @param iface the name of the Ethernet interface to send/receive on 25 | */ 26 | EtherSia_LinuxSocket(const char* iface = NULL); 27 | 28 | // Tell the compiler we want to use begin() from the base class 29 | using EtherSia::begin; 30 | 31 | /** 32 | * Initialise the Ethernet controller 33 | * Must be called before sending or receiving Ethernet frames 34 | * 35 | * @param address the local MAC address for the Ethernet interface 36 | * @return Returns true if setting up the Ethernet interface was successful 37 | */ 38 | virtual boolean begin(const MACAddress &address); 39 | 40 | /** 41 | * Send an Ethernet frame 42 | * @param data a pointer to the data to send 43 | * @param datalen the length of the data in the packet 44 | * @return the number of bytes transmitted 45 | */ 46 | virtual uint16_t sendFrame(const uint8_t *data, uint16_t datalen); 47 | 48 | /** 49 | * Read an Ethernet frame 50 | * @param buffer a pointer to a buffer to write the packet to 51 | * @param bufsize the available space in the buffer 52 | * @return the length of the received packet 53 | * or 0 if no packet was received 54 | */ 55 | virtual uint16_t readFrame(uint8_t *buffer, uint16_t bufsize); 56 | 57 | /** 58 | * Close the raw ethernet socket 59 | */ 60 | virtual void end(); 61 | 62 | protected: 63 | 64 | char ifname[IFNAMSIZ]; 65 | int ifindex; 66 | int sockfd; 67 | }; 68 | 69 | #endif /* LINUXSOCKET_H */ 70 | -------------------------------------------------------------------------------- /examples/SNTPClient/ntp.h: -------------------------------------------------------------------------------- 1 | 2 | /** The UDP port number to send NTP packets to */ 3 | const uint8_t NTP_PORT = 123; 4 | 5 | /** Data structure for an NTP packet, based on RFC4330 */ 6 | typedef struct ntpStructure { 7 | /** Leap Indicator, Version Number and Mode fields */ 8 | uint8_t flags; 9 | 10 | /** Stratum number; distance from a primary reference time source */ 11 | uint8_t stratum; 12 | 13 | /** Max Poll interval (exponent of 2 in seconds) */ 14 | uint8_t poll; 15 | 16 | /** Precision of the system clock (exponent of 2 in seconds) */ 17 | int8_t precision; 18 | 19 | /** Total roundtrip delay to the primary reference source */ 20 | int32_t rootDelay; 21 | 22 | /** Maximum error due to the clock frequency tolerance */ 23 | uint32_t rootDispersion; 24 | 25 | /** Identifier for the upstream reference source */ 26 | uint32_t referenceIdentifer; 27 | 28 | /** The time the server clock was last set or corrected (in seconds)*/ 29 | uint32_t referenceTimestampSeconds; 30 | 31 | /** The time the server clock was last set or corrected (fractions of a second) */ 32 | uint32_t referenceTimestampFraction; 33 | 34 | /** The time the the request departed the client for the server (in seconds) */ 35 | uint32_t originateTimestampSeconds; 36 | 37 | /** The time the the request departed the client for the server (fractions of a second) */ 38 | uint32_t originateTimestampFraction; 39 | 40 | /** The time at which the request arrived at the server or the reply arrived at 41 | the client (in seconds) */ 42 | uint32_t receiveTimestampSeconds; 43 | 44 | /** The time at which the request arrived at the server or the reply arrived at 45 | the client (fractions of a second) */ 46 | uint32_t receiveTimestampFraction; 47 | 48 | /** The time at which the request departed the client or the reply departed 49 | the server (in seconds) */ 50 | uint32_t transmitTimestampSeconds; 51 | 52 | /** The time at which the request departed the client or the reply departed 53 | the server (fractions of a second) */ 54 | uint32_t transmitTimestampFraction; 55 | } __attribute__((__packed__)) ntpType; 56 | 57 | -------------------------------------------------------------------------------- /examples/LinuxPacketPrinter/LinuxPacketPrinter.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Linux Packet Printer - prints Ethernet and IPv6 packet headers on Linux (not Arduino) 3 | * 4 | * This example demonstrates that it is possible to run EtherSia on Linux, using 5 | * a raw socket to send and receive Ethernet frames. 6 | * 7 | * Type `make` in the LinuxPacketPrinter directory to build this example. 8 | * Then type `sudo ./LinuxPacketPrinter` to run the example. 9 | * 10 | * It must be run as root, so that the program has permission to send and 11 | * receive Ethernet frames. 12 | * 13 | * Uses a static MAC address, please update with your own. 14 | * 15 | * Get your own Random Locally Administered MAC Address here: 16 | * https://www.hellion.org.uk/cgi-bin/randmac.pl 17 | * 18 | * @file 19 | */ 20 | 21 | #include 22 | 23 | /** Ethernet Interface to use */ 24 | EtherSia_LinuxSocket ether("eth0"); 25 | 26 | 27 | /** 28 | * Main function in Linux Packet Printer example 29 | * 30 | * @return 0 if successful 31 | */ 32 | int main() 33 | { 34 | MACAddress macAddress("5e:73:f9:8a:cf:ba"); 35 | 36 | Serial.println("[EtherSia LinuxPacketPrinter]"); 37 | Serial.print("Our MAC is: "); 38 | macAddress.println(); 39 | 40 | if (ether.begin(macAddress) == false) { 41 | Serial.println("Failed to configure Ethernet"); 42 | } 43 | 44 | Serial.print("Link Local Address: "); 45 | ether.linkLocalAddress().println(); 46 | Serial.print("Global Address: "); 47 | ether.globalAddress().println(); 48 | 49 | while (true) { 50 | if (ether.receivePacket()) { 51 | IPv6Packet& packet = ether.packet(); 52 | 53 | Serial.print("Source MAC: "); 54 | packet.etherSource().println(); 55 | 56 | Serial.print("Destination MAC: "); 57 | packet.etherDestination().println(); 58 | 59 | Serial.print("Source Address: "); 60 | packet.source().println(); 61 | 62 | Serial.print("Destination Address: "); 63 | packet.destination().println(); 64 | 65 | Serial.print("Protocol: "); 66 | Serial.println(packet.protocol(), DEC); 67 | 68 | Serial.print("Length: "); 69 | Serial.println(packet.payloadLength(), DEC); 70 | 71 | Serial.println(); 72 | } 73 | } 74 | 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /examples/PingClient/PingClient.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Example of sending ICMPv6 Ping packets periodically 3 | * 4 | * This example uses a static MAC address, please update with your own. 5 | * 6 | * Get your own Random Locally Administered MAC Address here: 7 | * https://www.hellion.org.uk/cgi-bin/randmac.pl 8 | * 9 | * @file 10 | */ 11 | 12 | #include 13 | 14 | /** Ethernet Interface (with Chip Select connected to Pin 10) */ 15 | EtherSia_ENC28J60 ether(10); 16 | 17 | /** Define Ping Client to send/receive packets */ 18 | PingClient ping(ether); 19 | 20 | void setup() { 21 | MACAddress macAddress("c2:6b:46:ef:30:4d"); 22 | 23 | // Setup serial port 24 | Serial.begin(115200); 25 | Serial.println("[EtherSia PingClient]"); 26 | 27 | // Start Ethernet 28 | if (ether.begin(macAddress) == false) { 29 | Serial.println("Failed to configure Ethernet"); 30 | } 31 | 32 | if (ping.setRemoteAddress("ipv6.aelius.com")) { 33 | Serial.print("Remote address: "); 34 | ping.remoteAddress().println(); 35 | } 36 | 37 | Serial.println("Ready."); 38 | } 39 | 40 | void loop() 41 | { 42 | // process packets 43 | ether.receivePacket(); 44 | 45 | // Is it time to send a ping? 46 | static unsigned long nextPing = millis(); 47 | if ((long)(millis() - nextPing) >= 0) { 48 | // Did the last ping timeout? 49 | if (ping.sequenceNumber() > 0 && ping.gotReply() == false) { 50 | Serial.print("Request timeout for icmp_seq "); 51 | Serial.println(ping.lastSequenceNumber(), DEC); 52 | } 53 | 54 | // Send a ping 55 | ping.send(); 56 | nextPing = millis() + 1000; 57 | } 58 | 59 | if (ping.havePacket()) { 60 | // We got a reply 61 | Serial.print(ether.packet().payloadLength()); 62 | Serial.print(" bytes from "); 63 | ether.packet().source().print(); 64 | 65 | Serial.print(", icmp_seq="); 66 | Serial.print(ping.lastSequenceNumber(), DEC); 67 | 68 | Serial.print(" hlim="); 69 | Serial.print(ether.packet().hopLimit(), DEC); 70 | 71 | float rtt = ping.lastRoundTripTime() / 1000.0; 72 | Serial.print(" time="); 73 | Serial.print(rtt); 74 | Serial.println(" ms"); 75 | } else { 76 | // Reject any incoming connections 77 | ether.rejectPacket(); 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /tests/70_check_syslog.tc: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "EtherSia.h" 3 | #include "hext.hh" 4 | #include "util.h" 5 | 6 | #suite Syslog 7 | 8 | #test construct_defaults 9 | EtherSia_Dummy ether; 10 | Syslog syslog(ether); 11 | ck_assert_int_eq(syslog.priority(), LOG_INFO); 12 | ck_assert_int_eq(syslog.facility(), LOG_LOCAL0); 13 | 14 | 15 | #test setPriority 16 | EtherSia_Dummy ether; 17 | Syslog syslog(ether); 18 | syslog.setPriority(LOG_CRIT); 19 | ck_assert_int_eq(syslog.priority(), LOG_CRIT); 20 | ck_assert_int_eq(syslog.facility(), LOG_LOCAL0); 21 | 22 | 23 | #test setFacility 24 | EtherSia_Dummy ether; 25 | Syslog syslog(ether); 26 | syslog.setFacility(LOG_DAEMON); 27 | ck_assert_int_eq(syslog.priority(), LOG_INFO); 28 | ck_assert_int_eq(syslog.facility(), LOG_DAEMON); 29 | 30 | 31 | #test println 32 | MACAddress routerMac = MACAddress("ca:2f:6d:70:f9:5f"); 33 | EtherSia_Dummy ether; 34 | ether.setGlobalAddress("2001:1234::1"); 35 | ether.setRouter(routerMac); 36 | ether.begin("00:04:a3:2c:2b:b9"); 37 | ether.clearSent(); 38 | 39 | Syslog syslog(ether); 40 | syslog.setRemoteAddress("2001:4321::514"); 41 | syslog.print("Hello"); 42 | syslog.println(" World"); 43 | 44 | HextFile expect("packets/udp_syslog_hello_world.hext"); 45 | frame_t &sent = ether.getLastSent(); 46 | ck_assert_int_eq(sent.length, expect.length); 47 | ck_assert_mem_eq(sent.packet, expect.buffer, expect.length); 48 | 49 | 50 | #test println_twice 51 | EtherSia_Dummy ether; 52 | ether.setGlobalAddress("2001:1234::1"); 53 | ether.begin("00:04:a3:2c:2b:b9"); 54 | ether.clearSent(); 55 | 56 | Syslog syslog(ether); 57 | syslog.setRemoteAddress("2001:4321::514"); 58 | syslog.println("First Message"); 59 | 60 | syslog.setPriority(LOG_DEBUG); 61 | syslog.print("The result=0x"); 62 | syslog.println(0x45, HEX); 63 | ck_assert_int_eq(ether.getSentCount(), 2); 64 | 65 | const int offset = ETHER_HEADER_LEN + IP6_HEADER_LEN + UDP_HEADER_LEN; 66 | 67 | frame_t &packet1 = ether.getSent(0); 68 | const char* log_line1 = "<134>EtherSia: First Message"; 69 | ck_assert_int_eq(packet1.length - offset, strlen(log_line1)+1); 70 | ck_assert_str_eq((const char*)packet1.packet + offset, log_line1); 71 | 72 | frame_t &packet2 = ether.getSent(1); 73 | const char* log_line2 = "<135>EtherSia: The result=0x45"; 74 | ck_assert_int_eq(packet2.length - offset, strlen(log_line2)+1); 75 | ck_assert_str_eq((const char*)packet2.packet + offset, log_line2); 76 | 77 | ether.end(); 78 | -------------------------------------------------------------------------------- /src/tcp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header file for the TCP data structures 3 | * @file tcp.h 4 | */ 5 | 6 | #ifndef TCP_H 7 | #define TCP_H 8 | 9 | #include 10 | 11 | 12 | /** 13 | * Structure for accessing the fields of a TCP packet header 14 | * @private 15 | */ 16 | struct tcp_header { 17 | uint16_t sourcePort; 18 | uint16_t destinationPort; 19 | uint32_t sequenceNum; 20 | uint32_t acknowledgementNum; 21 | uint8_t dataOffset; 22 | uint8_t flags; 23 | uint16_t window; 24 | uint16_t checksum; 25 | uint16_t urgentPointer; 26 | 27 | // Following fields are options used when sending/transmitting only 28 | uint8_t mssOptionKind; 29 | uint8_t mssOptionLen; 30 | uint16_t mssOptionValue; 31 | } __attribute__((__packed__)); 32 | 33 | /** 34 | * Enumeration for the values of tcp_header.flags 35 | * @private 36 | */ 37 | enum TCP_FLAGS { 38 | TCP_FLAG_URG = 0x20, ///< Urgent Pointer field is significant 39 | TCP_FLAG_ACK = 0x10, ///< Acknowledgment field is significant 40 | TCP_FLAG_PSH = 0x08, ///< Push function 41 | TCP_FLAG_RST = 0x04, ///< Reset the connection 42 | TCP_FLAG_SYN = 0x02, ///< Synchronise sequence numbers 43 | TCP_FLAG_FIN = 0x01 ///< No more data from sender 44 | }; 45 | 46 | /** 47 | * The minimum length of a TCP response packet without any extra options 48 | * @private 49 | */ 50 | #define TCP_MINIMUM_HEADER_LEN (20) 51 | 52 | /** 53 | * The length of a TCP response packet header, used when sending packets 54 | * @private 55 | */ 56 | #define TCP_TRANSMIT_HEADER_LEN (TCP_MINIMUM_HEADER_LEN + 4) 57 | 58 | /* Verify that compiler gets the structure size correct */ 59 | static_assert(sizeof(struct tcp_header) == TCP_TRANSMIT_HEADER_LEN, "Size is not correct"); 60 | 61 | /** 62 | * Get the length of the recieved TCP header in bytes 63 | * @private 64 | */ 65 | #define TCP_RECEIVE_HEADER_LEN ((TCP_HEADER_PTR->dataOffset & 0xF0) >> 2) 66 | 67 | /** 68 | * The maximum size of the TCP segment that we can receive 69 | * @private 70 | */ 71 | #define TCP_WINDOW_SIZE (ETHERSIA_MAX_PACKET_SIZE - \ 72 | ETHER_HEADER_LEN - IP6_HEADER_LEN - TCP_TRANSMIT_HEADER_LEN - 32) 73 | 74 | /** 75 | * Get the pointer to the TCP header from within EtherSia 76 | * @private 77 | */ 78 | #define TCP_HEADER_PTR ((struct tcp_header*)(packet.payload())) 79 | 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /tests/packets/icmp6_router_advertisment_with_dns.hext: -------------------------------------------------------------------------------- 1 | 33:33:00:00:00:01 # Ethernet Destination 2 | 3c:61:04:d4:8d:88 # Ethernet Source 3 | 86dd # EtherType (IPv6) 4 | 5 | 60 00 00 00 # IPv6 header 6 | 0078 # Length 7 | 3a # ICMPv6 Protocol 8 | ff # Hop Limit 9 | 10 | fe80:0000:0000:0000:3e61:04ff:fed4:8d88 # IPv6 Source Address 11 | ff02:0000:0000:0000:0000:0000:0000:0001 # IPv6 Destination Address 12 | 13 | 86 # ICMPv6 router advertisement (134) 14 | 00 # ICMPv6 Code 15 | 44dd # Checksum 16 | 17 | 40 # ICMPv6 RA: Current Hop Limit 18 | 00 # ICMPv6 RA: Flags 19 | 0384 # ICMPv6 RA: Router Lifetime 20 | 00000000 # ICMPv6 RA: Reachable Time 21 | 00000000 # ICMPv6 RA: Re-trans Timer 22 | 23 | 01 # ICMPv6 Option 1: Source Link-Layer Address 24 | 01 # ICMPv6 Option Length: 8 bytes 25 | 3c:61:04:d4:8d:88 # Router MAC 26 | 27 | 28 | 03 # ICMPv6 Option 3: Prefix Information 29 | 04 # ICMPv6 Option Length: 32 bytes 30 | 31 | 40 # ICMPv6 Prefix: Length 32 | c0 # ICMPv6 Prefix: Flags (On-link flag & Autoconf Flag) 33 | 00278d00 # ICMPv6 Prefix: Valid Lifetime (30 days) 34 | 00093a80 # ICMPv6 Prefix: Preferred Lifetime (7 days) 35 | 00000000 # ICMPv6 Prefix: Reserved 36 | 37 | 2001:08b0:ffd5:0003:0000:0000:0000:0000 # IPv6 prefix 38 | 39 | 40 | 19 # ICMPv6 Option 25: Recursive DNS Server 41 | 05 # ICMPv6 Option Length: 40 bytes 42 | 43 | 0000 # ICMPv6 RDNS: Reserved 44 | ffffffff # ICMPv6 RDNS: Lifetime - infinite 45 | 46 | 2001:08b0:0000:0000:0000:0000:0000:2020 # Recursive DNS Server Address 47 | 2001:08b0:0000:0000:0000:0000:0000:2021 # Recursive DNS Server Address 48 | 49 | 50 | 1F # ICMPv6 Option 31: DNS Search list 51 | 03 # ICMPv6 Option Length: 24 bytes 52 | 53 | 0000 # ICMPv6 DNS Search: Reserved 54 | ffffffff # ICMPv6 DNS Search: Lifetime - infinite 55 | 56 | "aelius.co.uk" # Domain name 57 | 00 00 00 00 # Padding 58 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | Release History 2 | =============== 3 | 4 | Version 2.4.0 (2017-08-28) 5 | -------------------------- 6 | - Added PingClient class and example 7 | - Added EtherSia::rejectPacket() 8 | - Added MACAddress::operator== 9 | - Added MACAddress::isIPv6Multicast() 10 | - Added checks for Ethernet source and destination address 11 | - Added support for constructing IPv6Address and MACAddress from a flash string 12 | - Switched to using SPISettings class to configure SPI 13 | - Set Traffic Class and Flow Label to 0 when sending replies 14 | - Changed TFTPServer::handleRequest() to return boolean 15 | - Use link local address when sending, if there is no global IP 16 | - Bug fix for Neighbour Discovery for link local address 17 | - Bug fix for neighbour discovery setting wrong source address 18 | 19 | 20 | Version 2.3.0 (2017-06-04) 21 | -------------------------- 22 | - Added 'Server: EtherSia' to HTTP responses 23 | - Added support for passing a flash string to setRemoteAddress() 24 | - MACAddress: added new constructor from 6-byte array 25 | 26 | 27 | Version 2.2.0 (2017-03-09) 28 | -------------------------- 29 | - Added TFTP server 30 | - Added Socket::transmitPayload() 31 | - Added IPv6Packet::etherType() 32 | - Made the Socket class a sub-class of Print 33 | - Added support for sending UDP packets to machines on the same subnet 34 | - Added example of manually configuring a static address 35 | - Added support for manually setting the local router address 36 | - Added EtherSia::disableAutoconfiguration() 37 | - Added EtherSia::routerMac() 38 | - Added EtherSia::inOurSubnet() 39 | - Added EtherSia::discoverNeighbour() 40 | - Created WebToggler example 41 | - Fix for not setting source MAC for ICMP6 NS and RS packets 42 | - Added EtherSia_Dummy driver class for testing EtherSia 43 | - Added HTTPServer::redirect() 44 | - Added methods for inspecting the body of an HTTP request 45 | - Lots of tests added 46 | 47 | 48 | Version 2.1.0 (2016-10-08) 49 | -------------------------- 50 | - Added support for W5500 chip 51 | - Added TCPServer and HTTPServer classes 52 | 53 | 54 | Version 2.0.0 (2016-09-11) 55 | -------------------------- 56 | - Added support for the W5100 Ethernet Controller in MACRaw mode 57 | - Added Syslog class and example 58 | - Refactored ENC28J60 class to be the child of EtherSia 59 | 60 | 61 | Version 1.0.0 (2016-07-31) 62 | -------------------------- 63 | - Initial Release 64 | - Neighbour Discovery Protocol / Stateless Autoconfiguration 65 | - UDP Client and Server 66 | - DNS Client 67 | -------------------------------------------------------------------------------- /src/UDPSocket.cpp: -------------------------------------------------------------------------------- 1 | #include "EtherSia.h" 2 | #include "util.h" 3 | 4 | UDPSocket::UDPSocket(EtherSia ðer) : Socket(ether) 5 | { 6 | } 7 | 8 | UDPSocket::UDPSocket(EtherSia ðer, uint16_t localPort) : Socket(ether, localPort) 9 | { 10 | } 11 | 12 | boolean UDPSocket::havePacket() 13 | { 14 | IPv6Packet& packet = _ether.packet(); 15 | 16 | if (!_ether.bufferContainsReceived()) { 17 | return false; 18 | } 19 | 20 | if (packet.protocol() != IP6_PROTO_UDP) { 21 | // Wrong protocol 22 | return false; 23 | } 24 | 25 | if (packetDestinationPort() != _localPort) { 26 | // Wrong destination port 27 | return false; 28 | } 29 | 30 | if (_remotePort && packetSourcePort() != _remotePort) { 31 | // Wrong source port 32 | return false; 33 | } 34 | 35 | if (!_ether.isOurAddress(packetDestination())) { 36 | // Wrong destination address 37 | return false; 38 | } 39 | 40 | if (!_remoteAddress.isZero() && packetSource() != _remoteAddress) { 41 | // Wrong source address 42 | return false; 43 | } 44 | 45 | // The packet in the buffer is valid for this socket 46 | return true; 47 | } 48 | 49 | void UDPSocket::sendInternal(uint16_t length, boolean isReply) 50 | { 51 | IPv6Packet& packet = _ether.packet(); 52 | struct udp_header *udpHeader = UDP_HEADER_PTR; 53 | uint16_t totalLen = UDP_HEADER_LEN + length; 54 | 55 | packet.setProtocol(IP6_PROTO_UDP); 56 | packet.setPayloadLength(totalLen); 57 | 58 | udpHeader->length = htons(totalLen); 59 | if (isReply) { 60 | udpHeader->destinationPort = udpHeader->sourcePort; 61 | } else { 62 | udpHeader->destinationPort = ntohs(_remotePort); 63 | } 64 | udpHeader->sourcePort = ntohs(_localPort); 65 | udpHeader->checksum = 0; 66 | udpHeader->checksum = htons(packet.calculateChecksum()); 67 | 68 | _ether.send(); 69 | } 70 | 71 | uint16_t UDPSocket::packetSourcePort() 72 | { 73 | IPv6Packet& packet = _ether.packet(); 74 | return ntohs(UDP_HEADER_PTR->sourcePort); 75 | } 76 | 77 | uint16_t UDPSocket::packetDestinationPort() 78 | { 79 | IPv6Packet& packet = _ether.packet(); 80 | return ntohs(UDP_HEADER_PTR->destinationPort); 81 | } 82 | 83 | uint8_t* UDPSocket::payload() 84 | { 85 | IPv6Packet& packet = _ether.packet(); 86 | return (uint8_t *)(packet.payload()) + UDP_HEADER_LEN; 87 | } 88 | 89 | uint16_t UDPSocket::payloadLength() 90 | { 91 | IPv6Packet& packet = _ether.packet(); 92 | return ntohs(UDP_HEADER_PTR->length) - UDP_HEADER_LEN; 93 | } 94 | -------------------------------------------------------------------------------- /examples/NanodeUDPClient/NanodeUDPClient.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Example of sending UDP packets from a Nanode board 3 | * 4 | * Sends a 'Hello Message' every 5 seconds to specificed IPv6 Address. 5 | * Remember to change the address you send to below in the setRemoteAddress() call. 6 | * 7 | * Use the socat command to receive packets sent by this sketch: 8 | * 9 | * socat -u UDP6-LISTEN:1234 STDOUT 10 | * 11 | * Requires the NanodeUNIO library: 12 | * https://github.com/sde1000/NanodeUNIO 13 | * 14 | * Nanode Hardware 15 | * --------------- 16 | * - CPU: Atmel ATMEGA328P 17 | * - Ethernet: Microchip ENC28J60 18 | * - MAC Address: Microchip 11AA02E48 serial EEPROM 19 | * 20 | * Nanode Pin Mapping 21 | * ------------------ 22 | * | Pin | Use | 23 | * |-----|------------------------------------| 24 | * | D6 | Red LED | 25 | * | D7 | UNI/O bus: MAC Address (11AA02E48) | 26 | * | D8 | SPI bus: Ethernet Slave Select | 27 | * | D11 | SPI bus: Shared MOSI | 28 | * | D12 | SPI bus: Shared MISO | 29 | * | D13 | SPI bus: Shared Serial Clock | 30 | * 31 | * @file 32 | */ 33 | 34 | #include 35 | #include 36 | 37 | /** Ethernet Interface (with Chip Select connected to Pin 8) */ 38 | EtherSia_ENC28J60 ether(8); 39 | 40 | /** Define UDP socket to send packets from */ 41 | UDPSocket udp(ether); 42 | 43 | void setup() { 44 | NanodeUNIO unio(NANODE_MAC_DEVICE); 45 | MACAddress macAddress; 46 | boolean r; 47 | 48 | // Setup serial port 49 | Serial.begin(38400); 50 | Serial.println("[EtherSia NanodeUDPClient]"); 51 | 52 | Serial.print("Reading MAC address... "); 53 | r = unio.read(macAddress, NANODE_MAC_ADDRESS, 6); 54 | if (r) Serial.println("success"); 55 | else Serial.println("failure"); 56 | 57 | macAddress.println(); 58 | 59 | // Start Ethernet 60 | if (ether.begin(macAddress) == false) { 61 | Serial.println("Failed to configure Ethernet"); 62 | } 63 | 64 | if (udp.setRemoteAddress("2001:41c8:51:7cf::6", 1234)) { 65 | Serial.print("Remote address: "); 66 | udp.remoteAddress().println(); 67 | } 68 | 69 | Serial.println("Ready."); 70 | } 71 | 72 | void loop() 73 | { 74 | // process packets 75 | ether.receivePacket(); 76 | 77 | static unsigned long nextMessage = millis(); 78 | if ((long)(millis() - nextMessage) >= 0) { 79 | Serial.println("Sending UDP."); 80 | udp.println("Hello World!"); 81 | udp.send(); 82 | nextMessage = millis() + 5000; 83 | } 84 | 85 | // Reject any incoming connections 86 | ether.rejectPacket(); 87 | } 88 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility functions that don't belong to a specific class 3 | * @file util.h 4 | */ 5 | 6 | #include 7 | 8 | #ifndef ETHERSIA_UTIL 9 | #define ETHERSIA_UTIL 10 | 11 | /** 12 | * Convert an ASCII hex character to its integer value 13 | * 14 | * @param c The character (0-9, a-f or A-F) 15 | * @return The integer value or -1 if it is invalid 16 | */ 17 | int8_t asciiToHex(char c); 18 | 19 | /** 20 | * Convert integer value into an ASCII hex string 21 | * 22 | * @param byte The integer value 23 | * @param str The string output - must be space for 2 bytes 24 | */ 25 | void hexToAscii(uint8_t byte, char *str); 26 | 27 | /** 28 | * Check if a string contains one or more colons 29 | * 30 | * @param str The string to check 31 | * @return True if the string contains at least one colon 32 | */ 33 | boolean containsColon(const char *str); 34 | 35 | /** 36 | * Print a 2-byte human readable hex value for an 8-bit integer 37 | * 38 | * @param byte The value to print (in range 0x00 to 0xFF) 39 | * @param p The stream to print to (default Serial) 40 | */ 41 | void printPaddedHex(uint8_t byte, Print &p=Serial); 42 | 43 | /** 44 | * Print a 4-byte human readable hex value for an 16-bit integer 45 | * 46 | * @param word The value to print (in range 0x0000 to 0xFFFF) 47 | * @param p The stream to print to (default Serial) 48 | */ 49 | void printPaddedHex16(uint16_t word, Print &p=Serial); 50 | 51 | /** 52 | * Print an array of bytes in hex, as rows of 16 bytes 53 | * 54 | * @param bytes the array of bytes 55 | * @param len the number bytes in the array 56 | * @param p The stream to print to (default Serial) 57 | */ 58 | void printHexDump(const uint8_t bytes[], uint16_t len, Print &p=Serial); 59 | 60 | /** 61 | * Calculate a IP type 16-bit checksum for a buffer 62 | * 63 | * @param sum The current sum accumulator (or 0 for first call) 64 | * @param data A pointer to the data buffer to calculate checksum for 65 | * @param len The length of the data (in bytes) to perform checksum on 66 | * @return The calculated checksum 67 | */ 68 | uint16_t chksum(uint16_t sum, const uint8_t *data, uint16_t len); 69 | 70 | /** 71 | * Macro to make it easy to define AVR flash strings as static members of a class 72 | * 73 | * @param klass The name of the class 74 | * @param var The name of the member variable 75 | * @param str The string literal 76 | */ 77 | #define FlashStringMaker(klass, var, str) \ 78 | static const char _fsm_##var[] PROGMEM = str; \ 79 | const __FlashStringHelper* klass::var = reinterpret_cast(_fsm_##var) 80 | 81 | 82 | /** 83 | * Macro to cast a previously defined string with the PROGMEM attribute to FlashStringHelper 84 | * 85 | * @param pstr_pointer Pointer to string with PROGMEM attribute 86 | */ 87 | #ifndef FPSTR 88 | #define FPSTR(pstr_pointer) (reinterpret_cast(pstr_pointer)) 89 | #endif 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/TCPServer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header file for the TCPServer class 3 | * @file TCPServer.h 4 | */ 5 | 6 | #ifndef TCPServer_H 7 | #define TCPServer_H 8 | 9 | #include 10 | #include "IPv6Packet.h" 11 | #include "Socket.h" 12 | #include "tcp.h" 13 | 14 | /** 15 | * Class for responding to TCP requests 16 | * 17 | * Requests and responses cannot be bigger than a single packet and 18 | * are limited by the size of the packet buffer. 19 | * 20 | * This class inherits from Print, so you you can also use the print() 21 | * and println() functions when composing a reply. 22 | * 23 | */ 24 | class TCPServer : public Socket { 25 | 26 | public: 27 | 28 | /** 29 | * Construct a TCP server, with a listening port defined 30 | * 31 | * @param ether The Ethernet interface to attach the server to 32 | * @param localPort The local TCP port number to listen on 33 | */ 34 | TCPServer(EtherSia ðer, uint16_t localPort); 35 | 36 | /** 37 | * Check if a TCP data packet is available to be processed for this server 38 | * 39 | * This method also has a side effect of responding to other stages 40 | * of the TCP sequence (the SYN and FIN packets). 41 | * 42 | * @return true if there is a valid packet with data has been received for this server 43 | */ 44 | boolean havePacket(); 45 | 46 | /** 47 | * Get the IPv6 source port number of the last TCP packet received 48 | * 49 | * @note Please call havePacket() first, before calling this method. 50 | * @return The source port number 51 | */ 52 | uint16_t packetSourcePort(); 53 | 54 | /** 55 | * Get the IPv6 destination port number of the last TCP packet received 56 | * 57 | * @note Please call havePacket() first, before calling this method. 58 | * @return The destination port number 59 | */ 60 | uint16_t packetDestinationPort(); 61 | 62 | /** 63 | * Get a pointer to the TCP payload of the last received packet 64 | * 65 | * @note Please call havePacket() first, before calling this method. 66 | * @return A pointer to the payload 67 | */ 68 | virtual uint8_t* payload(); 69 | 70 | /** 71 | * Get the length (in bytes) of the last received TCP packet payload 72 | * 73 | * @note Please call havePacket() first, before calling this method. 74 | * @return A pointer to the payload 75 | */ 76 | virtual uint16_t payloadLength(); 77 | 78 | /** 79 | * Get a pointer to the next TCP packet payload to be sent 80 | * 81 | * @return A pointer to the transmit payload buffer 82 | */ 83 | virtual uint8_t* transmitPayload(); 84 | 85 | protected: 86 | 87 | /** 88 | * Internal function to send a TCP packet, using data contained in buffer 89 | * 90 | * @param length The length of the data in the buffer 91 | */ 92 | virtual void sendInternal(uint16_t length, boolean isReply); 93 | 94 | }; 95 | 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /tests/70_check_dns.tc: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "EtherSia.h" 3 | #include "hext.hh" 4 | #include "util.h" 5 | #include "dns.h" 6 | 7 | #suite DNS 8 | 9 | 10 | #test dnsMakeRequest_ipv6_aelius 11 | HextFile expected("packets/dns_req_aelius.hext"); 12 | uint8_t buffer[256]; 13 | uint16_t len = dnsMakeRequest(buffer, "ipv6.aelius.com", 0x1234); 14 | ck_assert_int_eq(len, expected.length); 15 | ck_assert_mem_eq(buffer, expected.buffer, expected.length); 16 | 17 | 18 | #test dnsProcessReply_ipv6_aelius 19 | HextFile response("packets/dns_res_aelius.hext"); 20 | IPv6Address expect("2001:41c8:0051:07cf:0000:0000:0000:0006"); 21 | 22 | IPv6Address *addr = dnsProcessReply(response.buffer, response.length, 0x1234); 23 | ck_assert_ptr_ne(addr, NULL); 24 | ck_assert_mem_eq(expect, addr, sizeof(expect)); 25 | 26 | 27 | #test dnsProcessReply_id_mismatch 28 | HextFile response("packets/dns_res_aelius.hext"); 29 | IPv6Address *addr = dnsProcessReply(response.buffer, response.length, 0xFFFF); 30 | ck_assert_ptr_eq(addr, NULL); 31 | 32 | 33 | #test dnsProcessReply_error_code 34 | HextFile response("packets/dns_res_error.hext"); 35 | IPv6Address *addr = dnsProcessReply(response.buffer, response.length, 0x2a45); 36 | ck_assert_ptr_eq(addr, NULL); 37 | 38 | 39 | #test dnsProcessReply_truncated_query 40 | HextFile response("packets/dns_res_aelius.hext"); 41 | IPv6Address *addr = dnsProcessReply(response.buffer, 24, 0x1234); 42 | ck_assert_ptr_eq(addr, NULL); 43 | 44 | 45 | #test dnsProcessReply_no_AAAA 46 | HextFile response("packets/dns_res_no_aaaa.hext"); 47 | IPv6Address *addr = dnsProcessReply(response.buffer, response.length, 0xe010); 48 | ck_assert_ptr_eq(addr, NULL); 49 | 50 | 51 | #test dnsProcessReply_with_cnames 52 | HextFile response("packets/dns_res_long.hext"); 53 | IPv6Address expect("2a02:26f0:5d:2a8::1aca"); 54 | IPv6Address *addr = dnsProcessReply(response.buffer, response.length, 0xbb91); 55 | ck_assert_ptr_ne(addr, NULL); 56 | ck_assert_mem_eq(expect, addr, sizeof(expect)); 57 | 58 | 59 | #test dnsProcessReply_truncated_response 60 | HextFile response("packets/dns_res_long.hext"); 61 | IPv6Address *addr = dnsProcessReply(response.buffer, 128, 0xbb91); 62 | ck_assert_ptr_eq(addr, NULL); 63 | 64 | 65 | #test lookupHostname 66 | MACAddress routerMac = MACAddress("ca:2f:6d:70:f9:5f"); 67 | EtherSia_Dummy ether; 68 | ether.setGlobalAddress("2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9"); 69 | ether.setRouter(routerMac); 70 | ether.begin("00:04:a3:2c:2b:b9"); 71 | ether.clearSent(); 72 | 73 | HextFile dnsResponsePacket("packets/udp_dns_response.hext"); 74 | ether.injectRecievedPacket(dnsResponsePacket.buffer, dnsResponsePacket.length); 75 | 76 | ether.lookupHostname("ipv6.aelius.com"); 77 | ck_assert_int_eq(ether.getSentCount(), 1); 78 | 79 | // Check the DNS request packet that was sent looks correct 80 | HextFile expect("packets/udp_dns_request.hext"); 81 | frame_t &sent = ether.getLastSent(); 82 | ck_assert_int_eq(sent.length, expect.length); 83 | ck_assert_mem_eq(sent.packet, expect.buffer, expect.length); 84 | ether.end(); 85 | -------------------------------------------------------------------------------- /src/dns.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "IPv6Address.h" 4 | 5 | /** How often to send DNS request packets */ 6 | #define DNS_REQUEST_TIMEOUT (3500) 7 | 8 | /** How many times to send DNS request packets */ 9 | #define DNS_REQUEST_ATTEMPTS (3) 10 | 11 | /** The UDP port number to send queries to */ 12 | #define DNS_PORT_NUMBER (53) 13 | 14 | /* DNS Header section format 15 | From RFC1035 section 4.1.1. 16 | 1 1 1 1 1 1 17 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 18 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 19 | | ID | 20 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 21 | |QR| Opcode |AA|TC|RD|RA| Z | RCODE | 22 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 23 | */ 24 | 25 | /** The QR bit is set to 0 for queries */ 26 | #define DNS_FLAG_QUERY (0) 27 | 28 | /** The QR bit is set to 1 for responses */ 29 | #define DNS_FLAG_RESPONSE (1 << 7) 30 | 31 | /** Authoritative Answer Flag */ 32 | #define DNS_FLAG_AA (1 << 2) 33 | 34 | /** TrunCation flag - set if packet has been trancated */ 35 | #define DNS_FLAG_TC (1 << 1) 36 | 37 | /** Recursion Desired */ 38 | #define DNS_FLAG_RD (1 << 0) 39 | 40 | /** Recursion Available */ 41 | #define DNS_FLAG_RA (1 << 7) 42 | 43 | /** Bit mask to extract the RCODE from flags2 byte */ 44 | #define DNS_RCODE_MASK (0x0F) 45 | 46 | 47 | enum dnsType { 48 | DNS_TYPE_A = 1, 49 | DNS_TYPE_CNAME = 5, 50 | DNS_TYPE_PTR = 12, 51 | DNS_TYPE_MX = 15, 52 | DNS_TYPE_TXT = 16, 53 | DNS_TYPE_AAAA = 28, 54 | DNS_TYPE_SRV = 33, 55 | DNS_TYPE_ANY = 255 56 | }; 57 | 58 | enum dnsClass { 59 | DNS_CLASS_IN = 1, 60 | DNS_CLASS_ANY = 255 61 | }; 62 | 63 | 64 | /** 65 | * Structure for accessing the header of a DNS request/response packet 66 | * @private 67 | */ 68 | struct dnsHeader { 69 | uint16_t id; 70 | uint8_t flags1; 71 | uint8_t flags2; 72 | uint16_t qdcount; 73 | uint16_t ancount; 74 | uint16_t nscount; 75 | uint16_t arcount; 76 | } __attribute__((__packed__)); 77 | 78 | /** 79 | * Structure for accessing a DNS response record header 80 | * @private 81 | */ 82 | struct dnsRecord { 83 | // Name field here 84 | uint16_t type; 85 | uint16_t klass; 86 | uint32_t ttl; 87 | uint16_t rdlength; 88 | // Data field here 89 | } __attribute__((__packed__)); 90 | 91 | 92 | /** 93 | * Write a DNS Request for a hostname into a buffer 94 | * @private 95 | */ 96 | uint16_t dnsMakeRequest(uint8_t *buffer, const char *hostname, uint16_t requestId); 97 | 98 | /** 99 | * Get the pointer a IPv6 Address from a DNS response 100 | * Returns NULL if it is not a valid response 101 | * @private 102 | */ 103 | IPv6Address* dnsProcessReply(const uint8_t* payload, uint16_t length, uint16_t requestId); 104 | -------------------------------------------------------------------------------- /docs/generate-keywords.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # 3 | # Perl script to generate the Arduino IDE keywords.txt file 4 | # from the output of a Doxygen Tag File (GENERATE_TAGFILE) 5 | # 6 | # Copyright (c) 2018, Nicholas Humfrey 7 | # License: The 3-Clause BSD License 8 | # 9 | 10 | use XML::XPath; 11 | use XML::XPath::XMLParser; 12 | 13 | my $inputfile = $ARGV[0] || die "Usage: generate-keywords.pl tags.xml\n"; 14 | my $xp = XML::XPath->new(filename => $inputfile); 15 | 16 | # We use hashes as a way of maintaining a unique list 17 | my %classes = (); 18 | my %functions = (); 19 | my %constants = (); 20 | 21 | 22 | my $compounds = $xp->find("/tagfile/compound"); 23 | foreach my $compound ($compounds->get_nodelist) { 24 | my $kind = $compound->getAttribute('kind'); 25 | if ($kind eq 'file') { 26 | # Ignore the example files 27 | next if ($xp->find('./path', $compound)->string_value =~ /\/examples\//); 28 | 29 | my $classNodes = $xp->find("./class", $compound); 30 | foreach my $class ($classNodes->get_nodelist) { 31 | if ($class->getAttribute('kind') eq 'class') { 32 | $classes{$class->string_value} = 1; 33 | } 34 | } 35 | 36 | my $memberNodes = $xp->find("./member", $compound); 37 | foreach my $member ($memberNodes->get_nodelist) { 38 | if ($member->getAttribute('kind') eq 'define') { 39 | my $name = $xp->find('./name', $member)->string_value; 40 | $constants{$name} = 1; 41 | } 42 | } 43 | } elsif ($kind eq 'class') { 44 | my $name = $xp->find('./name', $compound)->string_value; 45 | next unless (exists $classes{$name}); 46 | 47 | my $memberNodes = $xp->find("./member", $compound); 48 | foreach my $member ($memberNodes->get_nodelist) { 49 | my $kind = $member->getAttribute('kind'); 50 | my $name = $xp->find('./name', $member)->string_value; 51 | my $type = $xp->find('type', $member)->string_value; 52 | if ($kind eq 'function') { 53 | next if $name =~ /^operator\W/; 54 | next if exists $classes{$name}; 55 | $functions{$name} = 1; 56 | } elsif ($kind eq 'variable' and $type =~ /^const /) { 57 | $constants{$name} = 1; 58 | } 59 | } 60 | } 61 | } 62 | 63 | print "#######################################\n"; 64 | print "# Syntax Coloring Map\n"; 65 | print "#######################################\n"; 66 | print "\n"; 67 | 68 | print "#######################################\n"; 69 | print "# Datatypes (KEYWORD1)\n"; 70 | print "#######################################\n"; 71 | 72 | foreach my $class (sort keys %classes) { 73 | print "$class\tKEYWORD1\n"; 74 | } 75 | 76 | print "\n\n"; 77 | print "#######################################\n"; 78 | print "# Methods and Functions (KEYWORD2)\n"; 79 | print "#######################################\n"; 80 | 81 | foreach my $function (sort keys %functions) { 82 | print "$function\tKEYWORD2\n"; 83 | } 84 | 85 | print "\n\n"; 86 | print "#######################################\n"; 87 | print "# Constants (LITERAL1)\n"; 88 | print "#######################################\n"; 89 | 90 | foreach my $constant (sort keys %constants) { 91 | print "$constant\tLITERAL1\n"; 92 | } 93 | 94 | print "\n" 95 | -------------------------------------------------------------------------------- /src/MACAddress.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "MACAddress.h" 3 | #include "util.h" 4 | 5 | MACAddress::MACAddress() 6 | { 7 | memset(_address, 0, sizeof(_address)); 8 | } 9 | 10 | MACAddress::MACAddress(uint8_t one, uint8_t two, uint8_t three, uint8_t four, uint8_t five, uint8_t six) 11 | { 12 | _address[0] = one; 13 | _address[1] = two; 14 | _address[2] = three; 15 | _address[3] = four; 16 | _address[4] = five; 17 | _address[5] = six; 18 | } 19 | 20 | MACAddress::MACAddress(const byte macaddr[6]) 21 | { 22 | memcpy(_address, macaddr, sizeof(_address)); 23 | } 24 | 25 | MACAddress::MACAddress(const char *macstr) 26 | { 27 | fromString(macstr); 28 | } 29 | 30 | MACAddress::MACAddress(const __FlashStringHelper *macstr) 31 | { 32 | fromString(macstr); 33 | } 34 | 35 | MACAddress::operator uint8_t*() 36 | { 37 | return _address; 38 | } 39 | 40 | boolean MACAddress::operator==(const MACAddress& address) const 41 | { 42 | return memcmp(_address, address._address, sizeof(_address)) == 0; 43 | } 44 | 45 | boolean MACAddress::operator!=(const MACAddress& address) const 46 | { 47 | return !(*this == address); 48 | } 49 | 50 | // See RFC2464 section 7 51 | void MACAddress::setIPv6Multicast(const uint8_t *address) 52 | { 53 | _address[0] = 0x33; 54 | _address[1] = 0x33; 55 | _address[2] = address[12]; 56 | _address[3] = address[13]; 57 | _address[4] = address[14]; 58 | _address[5] = address[15]; 59 | } 60 | 61 | boolean MACAddress::isIPv6Multicast() 62 | { 63 | return _address[0] == 0x33 && _address[1] == 0x33; 64 | } 65 | 66 | boolean MACAddress::fromString(const char *macstr) 67 | { 68 | uint8_t pos=0; 69 | 70 | for(uint8_t strpos=0; strpos < 17; strpos++) { 71 | uint8_t mod = strpos%3; 72 | if (mod == 2) { 73 | // should always be a colon or dash in the third position 74 | if (macstr[strpos] == ':' || macstr[strpos] == '-') { 75 | pos++; 76 | } else { 77 | // Fail 78 | return 0; 79 | } 80 | } else { 81 | int8_t val = asciiToHex(macstr[strpos]); 82 | if (val == -1) { 83 | // Fail 84 | return 0; 85 | } else if (mod == 0) { 86 | _address[pos] = val << 4; 87 | } else if (mod == 1) { 88 | _address[pos] |= val; 89 | } 90 | } 91 | } 92 | 93 | // Success 94 | return 1; 95 | } 96 | 97 | boolean MACAddress::fromString(const __FlashStringHelper *macstr) 98 | { 99 | char ramStr[18]; 100 | // Copy the string from flash program memory into RAM 101 | strcpy_P(ramStr, (const char*)macstr); 102 | return fromString(ramStr); 103 | } 104 | 105 | uint8_t MACAddress::operator[](int index) const 106 | { 107 | return _address[index]; 108 | } 109 | 110 | void MACAddress::print(Print &p) const 111 | { 112 | for (uint8_t i = 0; i < 6; ++i) { 113 | printPaddedHex(_address[i], p); 114 | if (i < 5) 115 | p.print(':'); 116 | } 117 | } 118 | 119 | void MACAddress::println(Print &p) const 120 | { 121 | this->print(p); 122 | p.println(); 123 | } 124 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include 3 | 4 | 5 | int8_t asciiToHex(char c) 6 | { 7 | c |= 0x20; 8 | 9 | if (c >= '0' && c <= '9') { 10 | return c - '0'; 11 | } else if (c >= 'a' && c <= 'f') { 12 | return (c - 'a') + 10; 13 | } else { 14 | return -1; 15 | } 16 | } 17 | 18 | void hexToAscii(uint8_t byte, char *str) 19 | { 20 | str[0] = (byte >> 4) & 0x0f; 21 | str[1] = byte & 0x0f; 22 | 23 | for (uint8_t i=0; i<2; i++) { 24 | if (str[i] > 9) { 25 | str[i] += 'a' - 10; 26 | } else { 27 | str[i] += '0'; 28 | } 29 | } 30 | } 31 | 32 | boolean containsColon(const char *str) 33 | { 34 | for (const char *ptr = str; *ptr; ptr++) { 35 | if (*ptr == ':') 36 | return true; 37 | } 38 | 39 | return false; 40 | } 41 | 42 | void printPaddedHex(uint8_t byte, Print &p) 43 | { 44 | char str[2]; 45 | hexToAscii(byte, str); 46 | p.write(str[0]); 47 | p.write(str[1]); 48 | } 49 | 50 | void printPaddedHex16(uint16_t word, Print &p) 51 | { 52 | printPaddedHex((word & 0xFF00) >> 8, p); 53 | printPaddedHex((word & 0x00FF) >> 0, p); 54 | } 55 | 56 | static void printHexDumpAscii(const char* ascii, uint8_t count, Print &p) 57 | { 58 | uint8_t i; 59 | 60 | for(i=0; i < (15-count)*3; i++) { 61 | p.print(' '); 62 | } 63 | 64 | if (count < 7) { 65 | p.print(' '); 66 | } 67 | 68 | p.print(F(" |")); 69 | for(i=0; i<=count; i++) { 70 | p.print(ascii[i]); 71 | } 72 | 73 | p.println('|'); 74 | } 75 | 76 | void printHexDump(const uint8_t bytes[], uint16_t len, Print &p) 77 | { 78 | char ascii[16]; 79 | uint8_t mod=0; 80 | 81 | for(uint16_t i=0; i_identifier = random(); 8 | this->_sequenceNumber = 0; 9 | this->_gotReply = false; 10 | this->_timeLastSent = 0; 11 | this->_timeLastRecieved = 0; 12 | } 13 | 14 | boolean PingClient::setRemoteAddress(const char *remoteAddress) 15 | { 16 | return Socket::setRemoteAddress(remoteAddress, 0); 17 | } 18 | 19 | boolean PingClient::havePacket() 20 | { 21 | ICMPv6Packet& packet = (ICMPv6Packet&)_ether.packet(); 22 | 23 | if (!_ether.bufferContainsReceived()) { 24 | return false; 25 | } 26 | 27 | if (packet.protocol() != IP6_PROTO_ICMP6) { 28 | // Wrong protocol 29 | return false; 30 | } 31 | 32 | if (packet.type != ICMP6_TYPE_ECHO_REPLY) { 33 | // Wrong ICMPv6 type 34 | return false; 35 | } 36 | 37 | if (!_ether.isOurAddress(packetDestination())) { 38 | // Wrong destination address 39 | return false; 40 | } 41 | 42 | if (!_remoteAddress.isZero() && packetSource() != _remoteAddress) { 43 | // Wrong source address 44 | return false; 45 | } 46 | 47 | if (ntohs(packet.echo.identifier) != this->_identifier) { 48 | // Wrong ICMPv6 Echo identifier 49 | return false; 50 | } 51 | 52 | if (ntohs(packet.echo.sequenceNumber) != lastSequenceNumber()) { 53 | // Sequence numbers didn't match 54 | return false; 55 | } 56 | 57 | // Everthing matched up 58 | this->_gotReply = true; 59 | this->_timeLastRecieved = micros(); 60 | return true; 61 | } 62 | 63 | void PingClient::send() 64 | { 65 | uint8_t *payload = this->payload(); 66 | uint8_t n; 67 | 68 | // Add some pseudo-random data to the payload 69 | for(n=0; n<8; n++) { 70 | payload[n] = random(); 71 | } 72 | 73 | Socket::send(n, false /*isReply*/); 74 | } 75 | 76 | void PingClient::sendInternal(uint16_t length, boolean /*isReply*/) 77 | { 78 | ICMPv6Packet& packet = (ICMPv6Packet&)_ether.packet(); 79 | uint16_t totalLen = ICMP6_HEADER_LEN + ICMP6_ECHO_HEADER_LEN + length; 80 | 81 | packet.setProtocol(IP6_PROTO_ICMP6); 82 | packet.setPayloadLength(totalLen); 83 | 84 | packet.type = ICMP6_TYPE_ECHO; 85 | packet.code = 0; 86 | packet.echo.identifier = htons(this->_identifier); 87 | packet.echo.sequenceNumber = htons(this->_sequenceNumber); 88 | 89 | packet.checksum = 0; 90 | packet.checksum = htons(packet.calculateChecksum()); 91 | 92 | _ether.send(); 93 | 94 | // Increment the sequence number for the next packet 95 | this->_sequenceNumber++; 96 | this->_gotReply = false; 97 | this->_timeLastSent = micros(); 98 | this->_timeLastRecieved = 0; 99 | } 100 | 101 | uint8_t* PingClient::payload() 102 | { 103 | ICMPv6Packet& packet = (ICMPv6Packet&)_ether.packet(); 104 | return (uint8_t*)(packet.payload()) + ICMP6_HEADER_LEN + ICMP6_ECHO_HEADER_LEN; 105 | } 106 | 107 | uint16_t PingClient::payloadLength() 108 | { 109 | ICMPv6Packet& packet = (ICMPv6Packet&)_ether.packet(); 110 | return packet.payloadLength() - ICMP6_HEADER_LEN - ICMP6_ECHO_HEADER_LEN; 111 | } 112 | 113 | uint32_t PingClient::lastRoundTripTime() 114 | { 115 | return _timeLastRecieved - _timeLastSent; 116 | } 117 | 118 | uint32_t PingClient::lastSequenceNumber() 119 | { 120 | return _sequenceNumber - 1; 121 | } 122 | -------------------------------------------------------------------------------- /src/IPv6Packet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "EtherSia.h" 4 | #include "IPv6Packet.h" 5 | #include "util.h" 6 | 7 | IPv6Packet::IPv6Packet() 8 | { 9 | memset(this, 0, sizeof(IPv6Packet)); 10 | init(); 11 | } 12 | 13 | void IPv6Packet::init() 14 | { 15 | _etherType = ntohs(ETHER_TYPE_IPV6); 16 | _ver_tc = 0x60; // Version and Traffic Class 17 | _tc_fl = 0x00; // Traffic Class and Flow Label 18 | _flowLabel = 0; // Flow Label 19 | _hopLimit = IP6_DEFAULT_HOP_LIMIT; 20 | } 21 | 22 | uint8_t* IPv6Packet::payload() 23 | { 24 | return (uint8_t *)(this) + sizeof(IPv6Packet); 25 | } 26 | 27 | boolean IPv6Packet::isValid() 28 | { 29 | if (this->_etherType != ntohs(ETHER_TYPE_IPV6)) { 30 | return false; 31 | } 32 | 33 | // Check the version header 34 | if (this->version() != 6) { 35 | return false; 36 | } 37 | 38 | // Verify the packet checksum (it should add up to 0) 39 | if (calculateChecksum() != 0) { 40 | return false; 41 | } 42 | 43 | return true; 44 | } 45 | 46 | void IPv6Packet::invalidate() 47 | { 48 | this->_etherType = 0; 49 | } 50 | 51 | uint16_t IPv6Packet::etherType() const 52 | { 53 | return ntohs(_etherType); 54 | } 55 | 56 | uint8_t IPv6Packet::version() const 57 | { 58 | return (_ver_tc & 0xF0) >> 4; 59 | } 60 | 61 | uint16_t IPv6Packet::length() const 62 | { 63 | return ETHER_HEADER_LEN + IP6_HEADER_LEN + payloadLength(); 64 | } 65 | 66 | uint16_t IPv6Packet::payloadLength() const 67 | { 68 | return ntohs(_length); 69 | } 70 | 71 | void IPv6Packet::setPayloadLength(uint16_t length) 72 | { 73 | _length = htons(length); 74 | } 75 | 76 | uint8_t IPv6Packet::protocol() 77 | { 78 | return _protocol; 79 | } 80 | 81 | void IPv6Packet::setProtocol(uint8_t protocol) 82 | { 83 | _protocol = protocol; 84 | } 85 | 86 | uint8_t IPv6Packet::hopLimit() 87 | { 88 | return _hopLimit; 89 | } 90 | 91 | void IPv6Packet::setHopLimit(uint8_t hopLimit) 92 | { 93 | _hopLimit = hopLimit; 94 | } 95 | 96 | MACAddress& IPv6Packet::etherSource() 97 | { 98 | return _etherSource; 99 | } 100 | 101 | void IPv6Packet::setEtherSource(MACAddress& address) 102 | { 103 | _etherSource = address; 104 | } 105 | 106 | MACAddress& IPv6Packet::etherDestination() 107 | { 108 | return _etherDestination; 109 | } 110 | 111 | void IPv6Packet::setEtherDestination(MACAddress& address) 112 | { 113 | _etherDestination = address; 114 | } 115 | 116 | IPv6Address& IPv6Packet::source() 117 | { 118 | return _source; 119 | } 120 | 121 | void IPv6Packet::setSource(IPv6Address& address) 122 | { 123 | _source = address; 124 | } 125 | 126 | IPv6Address& IPv6Packet::destination() 127 | { 128 | return _destination; 129 | } 130 | 131 | void IPv6Packet::setDestination(IPv6Address& address) 132 | { 133 | _destination = address; 134 | } 135 | 136 | 137 | // This function is derived from Contiki's uip6.c / upper_layer_chksum() 138 | uint16_t IPv6Packet::calculateChecksum() 139 | { 140 | /* First sum pseudoheader. */ 141 | /* IP protocol and length fields. This addition cannot carry. */ 142 | volatile uint16_t newsum = payloadLength() + protocol(); 143 | 144 | /* Sum IP source and destination addresses. */ 145 | newsum = chksum(newsum, (uint8_t *)(source()), 16); 146 | newsum = chksum(newsum, (uint8_t *)(destination()), 16); 147 | 148 | /* Sum the payload header and data */ 149 | newsum = chksum(newsum, payload(), payloadLength()); 150 | 151 | return ~newsum; 152 | } 153 | -------------------------------------------------------------------------------- /examples/NanodeUDPServer/NanodeUDPServer.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Example of receiving UDP packets from a Nanode board 3 | * 4 | * Allows you to control the Nanode LED by sending "on" and "off" UDP packets 5 | * 6 | * You can use the 'echo' and 'socat' commands to send UDP packets using: 7 | * 8 | * echo -n "on" | socat -u STDIN UDP-DATAGRAM::1234 9 | * echo -n "off" | socat -u STDIN UDP-DATAGRAM::1234 10 | * 11 | * 12 | * Requires the NanodeUNIO library: 13 | * https://github.com/sde1000/NanodeUNIO 14 | * 15 | * Nanode Hardware 16 | * --------------- 17 | * - CPU: Atmel ATMEGA328P 18 | * - Ethernet: Microchip ENC28J60 19 | * - MAC Address: Microchip 11AA02E48 serial EEPROM 20 | * 21 | * Nanode Pin Mapping 22 | * ------------------ 23 | * | Pin | Use | 24 | * |-----|------------------------------------| 25 | * | D6 | Red LED | 26 | * | D7 | UNI/O bus: MAC Address (11AA02E48) | 27 | * | D8 | SPI bus: Ethernet Slave Select | 28 | * | D11 | SPI bus: Shared MOSI | 29 | * | D12 | SPI bus: Shared MISO | 30 | * | D13 | SPI bus: Shared Serial Clock | 31 | * 32 | * @file 33 | */ 34 | 35 | #include 36 | #include 37 | 38 | /** The pin number the Red LED on Nanode is connected to */ 39 | const int NANODE_LED_PIN = 6; 40 | 41 | /** ENC28J60 Interface (with Chip Select connected to Pin 8) */ 42 | EtherSia_ENC28J60 ether(8); 43 | 44 | /** Define UDP socket and port number to listen on */ 45 | UDPSocket udp(ether, 1234); 46 | 47 | 48 | void setup() { 49 | NanodeUNIO unio(NANODE_MAC_DEVICE); 50 | MACAddress macAddress; 51 | boolean r; 52 | 53 | // Setup serial port 54 | Serial.begin(38400); 55 | Serial.println("[EtherSia NanodeUDPServer]"); 56 | 57 | Serial.print("Reading MAC address... "); 58 | r = unio.read(macAddress, NANODE_MAC_ADDRESS, 6); 59 | if (r) Serial.println("success"); 60 | else Serial.println("failure"); 61 | 62 | macAddress.println(); 63 | 64 | // Start Ethernet 65 | if (ether.begin(macAddress) == false) { 66 | Serial.println("Failed to configure Ethernet"); 67 | } 68 | 69 | Serial.print("Our link-local address is: "); 70 | ether.linkLocalAddress().println(); 71 | Serial.print("Our global address is: "); 72 | ether.globalAddress().println(); 73 | 74 | // Turn off the Red LED (it is connected to +5v) 75 | pinMode(NANODE_LED_PIN, OUTPUT); 76 | digitalWrite(NANODE_LED_PIN, HIGH); 77 | 78 | Serial.println("Ready."); 79 | } 80 | 81 | void loop() { 82 | ether.receivePacket(); 83 | 84 | if (udp.havePacket()) { 85 | Serial.print("Received UDP from: "); 86 | udp.packetSource().println(); 87 | 88 | Serial.print("Packet length: "); 89 | Serial.println(udp.payloadLength(), DEC); 90 | 91 | if (udp.payloadEquals("on")) { 92 | Serial.println("** LED On **"); 93 | digitalWrite(NANODE_LED_PIN, LOW); 94 | udp.sendReply("ok"); 95 | } else if (udp.payloadEquals("off")) { 96 | Serial.println("** LED Off **"); 97 | digitalWrite(NANODE_LED_PIN, HIGH); 98 | udp.sendReply("ok"); 99 | } else { 100 | Serial.println("Invalid Message"); 101 | udp.sendReply("err"); 102 | } 103 | } else { 104 | // Send back a ICMPv6 Destination Unreachable response 105 | // to any other connection attempts 106 | ether.rejectPacket(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/UDPSocket.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header file for the UDPSocket class 3 | * @file UDPSocket.h 4 | */ 5 | 6 | #ifndef UDPSocket_H 7 | #define UDPSocket_H 8 | 9 | #include 10 | #include "IPv6Packet.h" 11 | #include "Socket.h" 12 | 13 | class EtherSia; 14 | 15 | /** 16 | * Class for sending and receiving UDP packets on a specified port 17 | */ 18 | class UDPSocket: public Socket { 19 | 20 | public: 21 | 22 | /** 23 | * Construct a UDP socket 24 | * The local port number will be set to a random port number 25 | * 26 | * @param ether The Ethernet interface to attach the socket to 27 | */ 28 | UDPSocket(EtherSia ðer); 29 | 30 | /** 31 | * Construct a UDP socket, with a listening port defined 32 | * 33 | * @param ether The Ethernet interface to attach the socket to 34 | * @param localPort The local UDP port number to listen on 35 | */ 36 | UDPSocket(EtherSia ðer, uint16_t localPort); 37 | 38 | /** 39 | * Check if a UDP packet is available to be processed on this socket 40 | * @return true if there is a valid packet has been received for this socket 41 | */ 42 | boolean havePacket(); 43 | 44 | /** 45 | * Get the IPv6 source port number of the last UDP packet received 46 | * 47 | * @note Please call havePacket() first, before calling this method. 48 | * @return The source port number 49 | */ 50 | uint16_t packetSourcePort(); 51 | 52 | /** 53 | * Get the IPv6 destination port number of the last UDP packet received 54 | * 55 | * @note Please call havePacket() first, before calling this method. 56 | * @return The destination port number 57 | */ 58 | uint16_t packetDestinationPort(); 59 | 60 | /** 61 | * Get a pointer to the UDP payload of the current packet in the buffer 62 | * 63 | * @note Please call havePacket() first, before calling this method. 64 | * @return A pointer to the payload 65 | */ 66 | uint8_t* payload(); 67 | 68 | /** 69 | * Get the length (in bytes) of the UDP payload of the packet 70 | * 71 | * @note Please call havePacket() first, before calling this method. 72 | * @return A pointer to the payload 73 | */ 74 | uint16_t payloadLength(); 75 | 76 | protected: 77 | 78 | /** 79 | * Send a UDP packet, based on the contents of the buffer. 80 | * This function: 81 | * - sets the IP protocol number 82 | * - sets the IP packet length 83 | * - sets the UDP packet length 84 | * - sets the UDP source port number 85 | * - sets the UDP distination port number 86 | * - sets the UDP checksum 87 | * 88 | * @param length The length (in bytes) of the data to send 89 | */ 90 | void sendInternal(uint16_t length, boolean isReply); 91 | }; 92 | 93 | 94 | 95 | /** 96 | * Structure for accessing the fields of a UDP packet header 97 | * @private 98 | */ 99 | struct udp_header { 100 | uint16_t sourcePort; 101 | uint16_t destinationPort; 102 | uint16_t length; 103 | uint16_t checksum; 104 | } __attribute__((__packed__)); 105 | 106 | /** 107 | * The length of a UDP packet header 108 | * @private 109 | */ 110 | #define UDP_HEADER_LEN (8) 111 | 112 | /** 113 | * Get the pointer to the UDP header from within EtherSia 114 | * @private 115 | */ 116 | #define UDP_HEADER_PTR ((struct udp_header*)(packet.payload())) 117 | 118 | /* Verify that compiler gets the structure size correct */ 119 | static_assert(sizeof(struct udp_header) == UDP_HEADER_LEN, "Size is not correct"); 120 | 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /src/TCPServer.cpp: -------------------------------------------------------------------------------- 1 | #include "EtherSia.h" 2 | #include "util.h" 3 | 4 | TCPServer::TCPServer(EtherSia ðer, uint16_t localPort) : Socket(ether, localPort) 5 | { 6 | } 7 | 8 | boolean TCPServer::havePacket() 9 | { 10 | IPv6Packet& packet = _ether.packet(); 11 | struct tcp_header *tcpHeader = TCP_HEADER_PTR; 12 | 13 | if (!_ether.bufferContainsReceived()) { 14 | return false; 15 | } 16 | 17 | if (packet.protocol() != IP6_PROTO_TCP) { 18 | // Wrong protocol 19 | return false; 20 | } 21 | 22 | if (packetDestinationPort() != _localPort) { 23 | // Wrong destination port 24 | return false; 25 | } 26 | 27 | if (tcpHeader->flags & TCP_FLAG_RST) { 28 | return false; 29 | } 30 | 31 | if (tcpHeader->flags & TCP_FLAG_FIN) { 32 | tcpHeader->flags = TCP_FLAG_FIN | TCP_FLAG_ACK; 33 | sendReply((uint16_t)0); 34 | return false; 35 | } 36 | 37 | if (tcpHeader->flags & TCP_FLAG_SYN) { 38 | // Initialise our sequence number to a random number 39 | // (this is used later in sendInternal) 40 | tcpHeader->acknowledgementNum = random(); 41 | 42 | // Accept the connection 43 | tcpHeader->flags = TCP_FLAG_SYN | TCP_FLAG_ACK; 44 | sendReply((uint16_t)0); 45 | 46 | // We have handled the SYN 47 | return false; 48 | } 49 | 50 | // Packet contains data that needs to be handled 51 | if (payloadLength() > 0) { 52 | _writePos = -1; 53 | tcpHeader->flags = 0; 54 | return true; 55 | } 56 | 57 | // Something else? 58 | return false; 59 | } 60 | 61 | void TCPServer::sendInternal(uint16_t length, boolean /*isReply*/) 62 | { 63 | IPv6Packet& packet = _ether.packet(); 64 | struct tcp_header *tcpHeader = TCP_HEADER_PTR; 65 | 66 | uint32_t seq = tcpHeader->acknowledgementNum; 67 | uint32_t ack = ntohl(tcpHeader->sequenceNum); 68 | uint16_t receivedLen = payloadLength(); 69 | if (receivedLen == 0) 70 | receivedLen = 1; 71 | 72 | if (tcpHeader->flags == 0) { 73 | tcpHeader->flags = TCP_FLAG_ACK | TCP_FLAG_FIN | TCP_FLAG_PSH; 74 | } 75 | 76 | tcpHeader->destinationPort = tcpHeader->sourcePort; 77 | tcpHeader->sourcePort = htons(_localPort); 78 | tcpHeader->sequenceNum = seq; 79 | tcpHeader->acknowledgementNum = htonl(ack + receivedLen); 80 | 81 | tcpHeader->dataOffset = (TCP_TRANSMIT_HEADER_LEN / 4) << 4; 82 | tcpHeader->window = htons(TCP_WINDOW_SIZE); 83 | tcpHeader->urgentPointer = 0; 84 | 85 | tcpHeader->mssOptionKind = 2; 86 | tcpHeader->mssOptionLen = 4; 87 | tcpHeader->mssOptionValue = tcpHeader->window; 88 | 89 | packet.setPayloadLength(TCP_TRANSMIT_HEADER_LEN + length); 90 | 91 | tcpHeader->checksum = 0; 92 | tcpHeader->checksum = htons(packet.calculateChecksum()); 93 | 94 | _ether.send(); 95 | } 96 | 97 | uint16_t TCPServer::packetSourcePort() 98 | { 99 | IPv6Packet& packet = _ether.packet(); 100 | return ntohs(TCP_HEADER_PTR->sourcePort); 101 | } 102 | 103 | uint16_t TCPServer::packetDestinationPort() 104 | { 105 | IPv6Packet& packet = _ether.packet(); 106 | return ntohs(TCP_HEADER_PTR->destinationPort); 107 | } 108 | 109 | uint8_t* TCPServer::payload() 110 | { 111 | IPv6Packet& packet = _ether.packet(); 112 | return packet.payload() + TCP_RECEIVE_HEADER_LEN; 113 | } 114 | 115 | uint16_t TCPServer::payloadLength() 116 | { 117 | IPv6Packet& packet = _ether.packet(); 118 | return packet.payloadLength() - TCP_RECEIVE_HEADER_LEN; 119 | } 120 | 121 | uint8_t* TCPServer::transmitPayload() 122 | { 123 | IPv6Packet& packet = _ether.packet(); 124 | return packet.payload() + TCP_TRANSMIT_HEADER_LEN; 125 | } 126 | -------------------------------------------------------------------------------- /tests/20_check_mac_address.tc: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | 3 | #include "MACAddress.h" 4 | #suite MACAddress 5 | 6 | const uint8_t zero[6] = { 7 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 8 | }; 9 | 10 | #test new_MACAddress 11 | MACAddress addr; 12 | ck_assert_mem_eq(zero, addr, 6); 13 | 14 | 15 | #test new_MACAddress_six_uint8 16 | MACAddress addr(0x01, 0x02, 0x03, 0x04, 0x05, 0x06); 17 | ck_assert(addr[0] == 0x01); 18 | ck_assert(addr[1] == 0x02); 19 | ck_assert(addr[2] == 0x03); 20 | ck_assert(addr[3] == 0x04); 21 | ck_assert(addr[4] == 0x05); 22 | ck_assert(addr[5] == 0x06); 23 | 24 | 25 | #test new_MACAddress_byte_array 26 | byte array[6] = {0x4e, 0x27, 0xb0, 0xbe, 0x69, 0x24}; 27 | MACAddress addr(array); 28 | ck_assert_mem_eq(array, addr, 6); 29 | 30 | 31 | #test new_MACAddress_string 32 | uint8_t test[] = {0x4e, 0x27, 0xb0, 0xbe, 0x69, 0x24}; 33 | MACAddress addr("4e:27:b0:be:69:24"); 34 | ck_assert_mem_eq(test, addr, 6); 35 | 36 | 37 | #test new_MACAddress_flash_string 38 | uint8_t test[] = {0x4e, 0x27, 0xb0, 0xbe, 0x69, 0x24}; 39 | MACAddress addr(F("4e:27:b0:be:69:24")); 40 | ck_assert_mem_eq(test, addr, 6); 41 | 42 | 43 | #test fromString 44 | uint8_t test[] = {0x6a, 0x06, 0xed, 0x54, 0x7f, 0xd1}; 45 | MACAddress addr; 46 | ck_assert(addr.fromString("6a:06:ed:54:7f:d1") == true); 47 | ck_assert_mem_eq(test, addr, 6); 48 | 49 | 50 | #test fromString_flash 51 | uint8_t test[] = {0x6a, 0x06, 0xed, 0x54, 0x7f, 0xd1}; 52 | MACAddress addr; 53 | ck_assert(addr.fromString(F("6a:06:ed:54:7f:d1")) == true); 54 | ck_assert_mem_eq(test, addr, 6); 55 | 56 | 57 | #test fromString_dash 58 | uint8_t test[] = {0x6a, 0x06, 0xed, 0x54, 0x7f, 0xd1}; 59 | MACAddress addr; 60 | ck_assert(addr.fromString("6a-06-ed-54-7f-d1") == true); 61 | ck_assert_mem_eq(test, addr, 6); 62 | 63 | 64 | #test fromString_invalid_nonhex 65 | MACAddress addr; 66 | ck_assert(addr.fromString("6a:xx:xx:xx:xx:d1") == false); 67 | 68 | 69 | #test fromString_invalid_no_separator 70 | MACAddress addr; 71 | ck_assert(addr.fromString("6a06ed547fd1") == false); 72 | 73 | 74 | #test fromString_invalid_empty 75 | MACAddress addr; 76 | ck_assert(addr.fromString("") == false); 77 | 78 | 79 | #test fromString_invalid_foobar 80 | MACAddress addr; 81 | ck_assert(addr.fromString("foobar") == false); 82 | 83 | 84 | #test equal 85 | MACAddress addr1(0x10, 0x11, 0x12, 0x13, 0x14, 0x15); 86 | MACAddress addr2(0x10, 0x11, 0x12, 0x13, 0x14, 0x15); 87 | ck_assert(addr1 == addr2); 88 | 89 | #test not_equal 90 | MACAddress addr1(0x10, 0x11, 0x12, 0x13, 0x14, 0x15); 91 | MACAddress addr2(0x15, 0x14, 0x13, 0x12, 0x11, 0x10); 92 | ck_assert(addr1 != addr2); 93 | 94 | 95 | #test setIPv6Multicast 96 | uint8_t v6addr[] = { 97 | 0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, 98 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88 99 | }; 100 | uint8_t test[] = {0x33, 0x33, 0x00, 0x00, 0x88, 0x88}; 101 | MACAddress addr; 102 | addr.setIPv6Multicast(v6addr); 103 | ck_assert_mem_eq(test, addr, 6); 104 | 105 | #test isIPv6Multicast_true 106 | MACAddress addr(0x33, 0x33, 0x00, 0x00, 0x00, 0x02); 107 | ck_assert(addr.isIPv6Multicast()); 108 | 109 | #test isIPv6Multicast_false 110 | MACAddress addr(0x00, 0x33, 0x00, 0x00, 0x00, 0x02); 111 | ck_assert(!addr.isIPv6Multicast()); 112 | 113 | #test isIPv6Multicast_false_ipv4 114 | MACAddress addr(0x01, 0x00, 0x5e, 0x00, 0x00, 0xfb); 115 | ck_assert(!addr.isIPv6Multicast()); 116 | 117 | #test print 118 | Buffer buffer; 119 | MACAddress addr("4e:27:b0:be:69:24"); 120 | addr.print(buffer); 121 | ck_assert_str_eq(buffer, "4e:27:b0:be:69:24"); 122 | 123 | #test print_single_digit 124 | Buffer buffer; 125 | MACAddress addr("4e:27:b0:01:02:03"); 126 | addr.print(buffer); 127 | ck_assert_str_eq(buffer, "4e:27:b0:01:02:03"); 128 | 129 | #test println 130 | Buffer buffer; 131 | MACAddress addr("4e:27:b0:be:69:24"); 132 | addr.println(buffer); 133 | ck_assert_str_eq(buffer, "4e:27:b0:be:69:24\r\n"); 134 | -------------------------------------------------------------------------------- /tests/70_check_http.tc: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "EtherSia.h" 3 | #include "hext.hh" 4 | #include "util.h" 5 | 6 | #suite HTTP 7 | 8 | #test construct_defaults 9 | EtherSia_Dummy ether; 10 | HTTPServer http(ether); 11 | ck_assert_int_eq(http.localPort(), 80); 12 | 13 | 14 | #test isGet_root 15 | EtherSia_Dummy ether; 16 | ether.setGlobalAddress("2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9"); 17 | ether.begin("00:04:a3:2c:2b:b9"); 18 | ether.clearSent(); 19 | 20 | HTTPServer server(ether); 21 | HextFile http_get("packets/http_get_root.hext"); 22 | ether.injectRecievedPacket(http_get.buffer, http_get.length); 23 | ck_assert_int_eq(ether.receivePacket(), 196); 24 | ck_assert(server.havePacket() == true); 25 | ck_assert(server.isPost(F("/")) == false); 26 | ck_assert(server.isGet(F("/")) == true); 27 | ck_assert_int_eq(server.bodyLength(), 0); 28 | ck_assert(server.bodyEquals("foo") == false); 29 | 30 | 31 | #test isPost 32 | EtherSia_Dummy ether; 33 | ether.setGlobalAddress("2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9"); 34 | ether.begin("00:04:a3:2c:2b:b9"); 35 | ether.clearSent(); 36 | 37 | HTTPServer server(ether); 38 | HextFile http_post("packets/http_post_output1_off.hext"); 39 | ether.injectRecievedPacket(http_post.buffer, http_post.length); 40 | ck_assert_int_eq(ether.receivePacket(), 239); 41 | ck_assert(server.havePacket() == true); 42 | ck_assert(server.isGet(F("/")) == false); 43 | ck_assert(server.isPost(F("/")) == false); 44 | ck_assert(server.isPost(F("/output1")) == true); 45 | ck_assert_int_eq(server.bodyLength(), 3); 46 | ck_assert(server.bodyEquals("off") == true); 47 | 48 | 49 | #test sendReply 50 | EtherSia_Dummy ether; 51 | ether.setGlobalAddress("2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9"); 52 | ether.begin("00:04:a3:2c:2b:b9"); 53 | ether.clearSent(); 54 | 55 | HTTPServer server(ether); 56 | HextFile http_get("packets/http_get_root.hext"); 57 | ether.injectRecievedPacket(http_get.buffer, http_get.length); 58 | ck_assert_int_eq(ether.receivePacket(), 196); 59 | ck_assert(server.havePacket() == true); 60 | ck_assert(server.isGet(F("/")) == true); 61 | ck_assert_int_eq(0, ether.getSentCount()); 62 | 63 | server.printHeaders(server.typePlain); 64 | server.print(F("on")); 65 | server.sendReply(); 66 | HextFile expect("packets/http_response_plain_on.hext"); 67 | frame_t &sent = ether.getLastSent(); 68 | ck_assert_int_eq(sent.length, expect.length); 69 | ck_assert_mem_eq(sent.packet, expect.buffer, expect.length); 70 | ether.end(); 71 | 72 | 73 | #test sendReply_notFound 74 | EtherSia_Dummy ether; 75 | ether.setGlobalAddress("2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9"); 76 | ether.begin("00:04:a3:2c:2b:b9"); 77 | ether.clearSent(); 78 | 79 | HTTPServer server(ether); 80 | HextFile http_get("packets/http_get_root.hext"); 81 | ether.injectRecievedPacket(http_get.buffer, http_get.length); 82 | ck_assert_int_eq(ether.receivePacket(), 196); 83 | ck_assert(server.havePacket() == true); 84 | ck_assert_int_eq(0, ether.getSentCount()); 85 | 86 | server.notFound(); 87 | HextFile expect("packets/http_response_not_found.hext"); 88 | frame_t &sent = ether.getLastSent(); 89 | ck_assert_int_eq(sent.length, expect.length); 90 | ck_assert_mem_eq(sent.packet, expect.buffer, expect.length); 91 | ether.end(); 92 | 93 | 94 | #test sendReply_redirect 95 | EtherSia_Dummy ether; 96 | ether.setGlobalAddress("2001:08b0:ffd5:0003:0204:a3ff:fe2c:2bb9"); 97 | ether.begin("00:04:a3:2c:2b:b9"); 98 | ether.clearSent(); 99 | 100 | HTTPServer server(ether); 101 | HextFile http_get("packets/http_get_root.hext"); 102 | ether.injectRecievedPacket(http_get.buffer, http_get.length); 103 | ck_assert_int_eq(ether.receivePacket(), 196); 104 | ck_assert(server.havePacket() == true); 105 | ck_assert_int_eq(0, ether.getSentCount()); 106 | 107 | server.redirect(F("/foo/bar")); 108 | HextFile expect("packets/http_response_redirect_foo_bar.hext"); 109 | frame_t &sent = ether.getLastSent(); 110 | ck_assert_int_eq(sent.length, expect.length); 111 | ck_assert_mem_eq(sent.packet, expect.buffer, expect.length); 112 | ether.end(); 113 | -------------------------------------------------------------------------------- /src/Syslog.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header file for the Syslog class 3 | * @file Syslog.h 4 | */ 5 | 6 | #ifndef Syslog_H 7 | #define Syslog_H 8 | 9 | #include "UDPSocket.h" 10 | #include 11 | 12 | /** An enumeration of valid Syslog priority values */ 13 | enum SyslogPriority { 14 | LOG_EMERG = 0, ///< Emergency: System is unusable 15 | LOG_ALERT = 1, ///< Alert: Should be corrected immediately 16 | LOG_CRIT = 2, ///< Critical: Critical conditions 17 | LOG_ERR = 3, ///< Error: Error conditions 18 | LOG_WARNING = 4, ///< Warning: May indicate that an error will occur if action is not taken 19 | LOG_NOTICE = 5, ///< Notice: Events that are unusual, but not error conditions 20 | LOG_INFO = 6, ///< Informational: Normal operational messages that require no action 21 | LOG_DEBUG = 7 ///< Debug: Information useful to developers for debugging the application 22 | }; 23 | 24 | /** An enumeration of valid Syslog facility numbers */ 25 | enum SyslogFacility { 26 | LOG_KERN = 0, ///< kernel messages 27 | LOG_USER = 1, ///< user-level messages 28 | LOG_MAIL = 2, ///< mail system 29 | LOG_DAEMON = 3, ///< system daemons 30 | LOG_AUTH = 4, ///< security/authorization messages 31 | 32 | LOG_LOCAL0 = 16, ///< no specific purpose, reserved for local use 33 | LOG_LOCAL1 = 17, ///< no specific purpose, reserved for local use 34 | LOG_LOCAL2 = 18, ///< no specific purpose, reserved for local use 35 | LOG_LOCAL3 = 19, ///< no specific purpose, reserved for local use 36 | LOG_LOCAL4 = 20, ///< no specific purpose, reserved for local use 37 | LOG_LOCAL5 = 21, ///< no specific purpose, reserved for local use 38 | LOG_LOCAL6 = 22, ///< no specific purpose, reserved for local use 39 | LOG_LOCAL7 = 23 ///< no specific purpose, reserved for local use 40 | }; 41 | 42 | 43 | 44 | /** 45 | * Class for sending logging messages to a remote log server over UDP. 46 | * The Print class is included - so messages can be sent using print() and println(), 47 | * in the same way as with the Serial object. 48 | * 49 | * Log lines will be sent upon writing a newline character (eg using println). 50 | * 51 | * @see https://en.wikipedia.org/wiki/Syslog 52 | */ 53 | class Syslog : public UDPSocket { 54 | 55 | public: 56 | /** Default UDP port number to sent Syslog messages to */ 57 | const uint16_t SysLogPortNumber = 514; 58 | 59 | /** 60 | * Construct a Syslog socket 61 | * 62 | * Set the remote server address using setRemoteAddress() 63 | * 64 | * @param ether The Ethernet interface to attach the socket to 65 | */ 66 | Syslog(EtherSia ðer); 67 | 68 | /** 69 | * Set the remote address to send the syslog packets to 70 | * 71 | * @param remoteAddress The remote address or hostname 72 | * @return true if the remote address was set successfully 73 | */ 74 | boolean setRemoteAddress(const char *remoteAddress); 75 | 76 | /** 77 | * Set the priority value for messages 78 | * 79 | * See the ::SyslogPriority enumeration for valid values. 80 | * 81 | * @param priority The priority value in the range 0-7 82 | */ 83 | void setPriority(uint8_t priority); 84 | 85 | /** 86 | * Get the configured priority value for messages 87 | * 88 | * See the ::SyslogPriority enumeration for valid values. 89 | * 90 | * @return The priority value in the range 0-7 91 | */ 92 | uint8_t priority(); 93 | 94 | /** 95 | * Set the facility number for messages 96 | * 97 | * See the ::SyslogFacility enumeration for valid values. 98 | * 99 | * @param facility The facility number in the range 0-25 100 | */ 101 | void setFacility(uint8_t facility); 102 | 103 | /** 104 | * Get the configured facility number for messages 105 | * 106 | * See the ::SyslogFacility enumeration for valid values. 107 | * 108 | * @return The priority value in the range 0-25 109 | */ 110 | uint8_t facility(); 111 | 112 | protected: 113 | 114 | boolean handleWriteNewline(); 115 | 116 | void writePayloadHeader(); 117 | 118 | uint8_t _pri; 119 | 120 | }; 121 | 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /src/TFTPServer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header file for the TFTPServer class 3 | * @file TFTPServer.h 4 | */ 5 | 6 | #ifndef TFTPServer_H 7 | #define TFTPServer_H 8 | 9 | #include 10 | #include "UDPSocket.h" 11 | 12 | /** 13 | * Class for responding to Trivial File Transfer Protocol (TFTP) requests 14 | * 15 | * Note that this class must be sub-classed before it can be used. 16 | * 17 | * In the subclass, the following methods must be implemented: 18 | * - openFile() 19 | * - writeBytes() 20 | * - readBytes() 21 | * 22 | * Warning: after a read or write request is initiated other packets are 23 | * ignored until the transfer is complete. 24 | * 25 | */ 26 | class TFTPServer: public UDPSocket { 27 | 28 | public: 29 | 30 | /** 31 | * Construct a TFTP server 32 | * 33 | * @param ether The Ethernet interface to attach the TFTP server to 34 | * @param localPort The local UDP port number to listen on (default is 69) 35 | */ 36 | TFTPServer(EtherSia ðer, uint16_t localPort=69); 37 | 38 | /** 39 | * Handle TFTP packets 40 | * 41 | * This method must be called to check the packets in loop() after receivePacket() 42 | * 43 | * @return Returns true if the incoming packet was a TFTP one 44 | */ 45 | boolean handleRequest(); 46 | 47 | 48 | /// The maximum size of payload in a DATA packet 49 | const uint16_t TFTP_BLOCK_SIZE = 512; 50 | 51 | /// How long to wait for a DATA packet 52 | /// This has to be high because TFTP clients seem to take a long time to re-send packets 53 | const uint16_t TFTP_DATA_TIMEOUT = 10000; // 10 seconds 54 | 55 | /// How long to wait for an ACK packet before sending DATA again 56 | const uint16_t TFTP_ACK_TIMEOUT = 100; // 100 milliseconds 57 | 58 | /// How many times to attempt re-send of data packets that havn't been acknowledged 59 | const uint8_t TFTP_RETRIES = 10; 60 | 61 | protected: 62 | 63 | /** 64 | * Open/check that a filename is valid 65 | * 66 | * @param filename The filename from the TFTP request 67 | * @return a positive fileno if successful, a negative number if unsuccessful 68 | */ 69 | virtual int8_t openFile(const char* filename) = 0; 70 | 71 | /** 72 | * Write bytes received in a TFTP transfer 73 | * 74 | * This method is called once for each block received. 75 | * 76 | * @param fileno The file number being written to (as returned by openFile) 77 | * @param block The TFTP block number (starting at 1) 78 | * @param data A pointer to the bytes to be written 79 | * @param len The number of bytes in the block 80 | */ 81 | virtual void writeBytes(int8_t fileno, uint16_t block, const uint8_t* data, uint16_t len) = 0; 82 | 83 | /** 84 | * Read bytes requested in a TFTP transfer 85 | * 86 | * This method is called once for each block to be sent. 87 | * 88 | * @param fileno The file number being read from (as returned by openFile) 89 | * @param block The TFTP block number (starting at 1) 90 | * @param data A pointer to the buffer to copy to read data into 91 | * @return the length of the current block 92 | * (if less than TFTP_BLOCK_SIZE, then it is the last block in the transfer) 93 | */ 94 | virtual int16_t readBytes(int8_t fileno, uint16_t block, uint8_t* data) = 0; 95 | 96 | 97 | 98 | enum { 99 | TFTP_OPCODE_READ = 1, 100 | TFTP_OPCODE_WRITE = 2, 101 | TFTP_OPCODE_DATA = 3, 102 | TFTP_OPCODE_ACK = 4, 103 | TFTP_OPCODE_ERROR = 5 104 | }; 105 | 106 | enum { 107 | TFTP_UNDEFINED_ERROR = 0, 108 | TFTP_NOT_FOUND = 1, 109 | TFTP_ACCESS_VIOLATION = 2, 110 | TFTP_DISK_FULL = 3, 111 | TFTP_ILLEGAL_OPERATION = 4, 112 | TFTP_UNKNOWN_TRANSFER_ID = 5, 113 | TFTP_FILE_ALREADY_EXISTS = 6, 114 | TFTP_NO_SUCH_USER = 7, 115 | }; 116 | 117 | void handleWriteRequest(int8_t fileno, IPv6Address& address, uint16_t port); 118 | void handleReadRequest(int8_t fileno, IPv6Address& address, uint16_t port); 119 | 120 | boolean waitForAck(UDPSocket &sock, uint16_t expectedBlock); 121 | void sendAck(UDPSocket &sock, uint16_t block); 122 | void sendError(uint8_t errorCode); 123 | 124 | }; 125 | 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /src/MACAddress.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header file for the MACAddress class 3 | * @file MACAddress.h 4 | */ 5 | 6 | #ifndef MACAddress_H 7 | #define MACAddress_H 8 | 9 | #include 10 | 11 | 12 | /** 13 | * Class for the storage and manipulation of 48-bit Ethernet addresses. 14 | */ 15 | class MACAddress { 16 | private: 17 | uint8_t _address[6]; 18 | 19 | public: 20 | /** 21 | * Constructor for a new / all-zero MAC address (00:00:00:00:00:00) 22 | */ 23 | MACAddress(); 24 | 25 | /** 26 | * Constructor for MAC address from a string. 27 | * @param macstr a human readable string containing a 17 character MAC address 28 | */ 29 | MACAddress(const char *macstr); 30 | 31 | /** 32 | * Constructor for MAC address from a Flash string (use the F() macro) 33 | * @param macstr a human readable string containing a 17 character MAC address 34 | */ 35 | MACAddress(const __FlashStringHelper *macstr); 36 | 37 | /** 38 | * Constructor for MAC address from an array of 6 bytes 39 | * @param macaddr a 48-bit (6 byte) MAC address 40 | */ 41 | MACAddress(const byte macaddr[6]); 42 | 43 | /** 44 | * Constructor for MAC address from 6-octets. 45 | * @param one The first octet of the address 46 | * @param two The second octet of the address 47 | * @param three The third octet of the address 48 | * @param four The fourth octet of the address 49 | * @param five The fifth octet of the address 50 | * @param six The sixth octet of the address 51 | */ 52 | MACAddress(uint8_t one, uint8_t two, uint8_t three, uint8_t four, uint8_t five, uint8_t six); 53 | 54 | /** 55 | * Parse a human readable MAC address into a MACAddress object. 56 | * @param macstr a human readable string containing a 17 character MAC address 57 | * @return true if successful, false if parsing failed 58 | */ 59 | boolean fromString(const char *macstr); 60 | 61 | /** 62 | * Parse a flash string MAC address into a MACAddress object (use the F() macro) 63 | * @param macstr a human readable string containing a 17 character MAC address 64 | * @return true if successful, false if parsing failed 65 | */ 66 | boolean fromString(const __FlashStringHelper *macstr); 67 | 68 | /** 69 | * Cast the MAC address to an array of octets. 70 | */ 71 | operator uint8_t*(); 72 | 73 | /** 74 | * Check if the address equals another MAC address. 75 | * @param address the second address to compare to 76 | * @return true if the two addresses are the same 77 | */ 78 | boolean operator==(const MACAddress& address) const; 79 | 80 | /** 81 | * Check if the address is not equal to another MAC address. 82 | * @param address the second address to compare to 83 | * @return true if the two addresses are the same 84 | */ 85 | boolean operator!=(const MACAddress& address) const; 86 | 87 | /** 88 | * Calculate the multicast MAC address for an IPv6 address. 89 | * @param address An IPv6 address as an array of 16-bytes 90 | */ 91 | void setIPv6Multicast(const uint8_t* address); 92 | 93 | /** 94 | * Check if the MAC address is an IPv6 multicast address. 95 | * An IPv6 multicast MAC looks like the pattern: 33:33:xx:xx:xx:xx 96 | * @return true if it is an IPv6 multicast address 97 | */ 98 | boolean isIPv6Multicast(); 99 | 100 | /** 101 | * Get an individual octet from the MAC address. 102 | * Indexed from 0 to 5. 103 | * @param index The byte number from the address to get 104 | * @return The requested byte from the address 105 | */ 106 | uint8_t operator[](int index) const; 107 | 108 | /** 109 | * Print a MAC address to a stream as a human readable string. 110 | * @param print The stream to print to (defaults to Serial) 111 | */ 112 | void print(Print &print=Serial) const; 113 | 114 | /** 115 | * Print a MAC address to a stream with line ending. 116 | * @param print The stream to print to (defaults to Serial) 117 | */ 118 | void println(Print &print=Serial) const; 119 | 120 | } __attribute__((__packed__)); 121 | 122 | /* Verify that compiler gets the structure size correct */ 123 | static_assert(sizeof(MACAddress) == 6, "Size is not correct"); 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /src/dummy.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header file for using EtherSia with a dummy Ethernet interface 3 | * @file LinuxSocket.h 4 | */ 5 | 6 | #ifndef DUMMY_H 7 | #define DUMMY_H 8 | 9 | #include "EtherSia.h" 10 | 11 | #include 12 | 13 | /** 14 | * Structure for storing information about sent and received packets 15 | * 16 | * Only used for testing with EtherSia_Dummy 17 | * 18 | * @private 19 | */ 20 | typedef struct frame_wrapper { 21 | time_t time; ///< A UNIX timestamp for the time the packet was sent/received 22 | uint16_t length; ///< The length of the packet (in bytes) 23 | IPv6Packet* packet; ///< A pointer to the packet data 24 | } frame_t; 25 | 26 | /** 27 | * A dummy Ethernet interface, that can be useful for testing 28 | * 29 | * @note this is probably only useful for the testing and development of EtherSia. 30 | */ 31 | class EtherSia_Dummy : public EtherSia { 32 | 33 | public: 34 | /** 35 | * Create a new dummy ethernet controller 36 | */ 37 | EtherSia_Dummy(); 38 | 39 | // Tell the compiler we want to use begin() from the base class 40 | using EtherSia::begin; 41 | 42 | /** 43 | * Initialise the Ethernet controller 44 | * Must be called before sending or receiving Ethernet frames 45 | * 46 | * @param address the local MAC address for the Ethernet interface 47 | * @return Returns true if setting up the Ethernet interface was successful 48 | */ 49 | virtual boolean begin(const MACAddress &address); 50 | 51 | /** 52 | * Send an Ethernet frame 53 | * @param data a pointer to the data to send 54 | * @param datalen the length of the data in the packet 55 | * @return the number of bytes transmitted 56 | */ 57 | virtual uint16_t sendFrame(const uint8_t *data, uint16_t datalen); 58 | 59 | /** 60 | * Read an Ethernet frame 61 | * @param buffer a pointer to a buffer to write the packet to 62 | * @param bufsize the available space in the buffer 63 | * @return the length of the received packet 64 | * or 0 if no packet was received 65 | */ 66 | virtual uint16_t readFrame(uint8_t *buffer, uint16_t bufsize); 67 | 68 | /** 69 | * Close the dummy ethernet socket 70 | * 71 | * Frees up memory used by packet buffers 72 | */ 73 | virtual void end(); 74 | 75 | /** 76 | * Add a packet into the buffer, to be received by EtherSia 77 | * 78 | * @param packet pointer to the packet to add to the receive list 79 | * @param length the length of the packet (in bytes) 80 | */ 81 | void injectRecievedPacket(void *packet, uint16_t length); 82 | 83 | /** 84 | * Clear the list of received packets 85 | */ 86 | void clearRecieved(); 87 | 88 | /** 89 | * Clear the list of sent packets 90 | */ 91 | void clearSent(); 92 | 93 | /** 94 | * Get a packet sent by EtherSia 95 | * 96 | * @param pos the packet number to return (by default the first) 97 | * @return A structure containing a pointer to the packet and its length 98 | */ 99 | frame_t& getSent(size_t pos=0); 100 | 101 | /** 102 | * Get the most recently sent packet by EtherSia 103 | * @return A structure containing a pointer to the packet and its length 104 | */ 105 | frame_t& getLastSent(); 106 | 107 | /** 108 | * Get number of packets that EtherSia has received 109 | * This includes invalid or ignored packets 110 | * @return the number of packets received 111 | */ 112 | size_t getRecievedCount() { 113 | return _recievedCount; 114 | } 115 | 116 | /** 117 | * Get number of packets made available to EtherSia to receive 118 | * @return the number of packets injected 119 | */ 120 | size_t getInjectCount() { 121 | return _injectCount; 122 | } 123 | 124 | /** 125 | * Get number of packets that EtherSia has sent 126 | * @return the number of packets sent 127 | */ 128 | size_t getSentCount() { 129 | return _sentCount; 130 | } 131 | 132 | protected: 133 | static const size_t bufferSize = 100; 134 | 135 | frame_t _recieved[bufferSize]; 136 | size_t _injectCount; 137 | size_t _recievedCount; 138 | frame_t _sent[bufferSize]; 139 | size_t _sentCount; 140 | 141 | }; 142 | 143 | 144 | #endif /* DUMMY_H */ 145 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | EtherSia KEYWORD1 9 | EtherSia_ENC28J60 KEYWORD1 10 | EtherSia_LinuxSocket KEYWORD1 11 | EtherSia_W5100 KEYWORD1 12 | EtherSia_W5500 KEYWORD1 13 | HTTPServer KEYWORD1 14 | IPv6Address KEYWORD1 15 | IPv6Packet KEYWORD1 16 | MACAddress KEYWORD1 17 | PingClient KEYWORD1 18 | Socket KEYWORD1 19 | Syslog KEYWORD1 20 | TCPServer KEYWORD1 21 | TFTPServer KEYWORD1 22 | UDPSocket KEYWORD1 23 | 24 | 25 | ####################################### 26 | # Methods and Functions (KEYWORD2) 27 | ####################################### 28 | begin KEYWORD2 29 | body KEYWORD2 30 | bodyEquals KEYWORD2 31 | bodyLength KEYWORD2 32 | bufferContainsReceived KEYWORD2 33 | calculateChecksum KEYWORD2 34 | destination KEYWORD2 35 | disableAutoconfiguration KEYWORD2 36 | discoverNeighbour KEYWORD2 37 | dnsServerAddress KEYWORD2 38 | enableAutoconfiguration KEYWORD2 39 | end KEYWORD2 40 | etherDestination KEYWORD2 41 | etherSource KEYWORD2 42 | etherType KEYWORD2 43 | facility KEYWORD2 44 | fromString KEYWORD2 45 | globalAddress KEYWORD2 46 | gotReply KEYWORD2 47 | handleRequest KEYWORD2 48 | havePacket KEYWORD2 49 | hopLimit KEYWORD2 50 | identifier KEYWORD2 51 | inOurSubnet KEYWORD2 52 | inSameSubnet KEYWORD2 53 | init KEYWORD2 54 | invalidate KEYWORD2 55 | isDelete KEYWORD2 56 | isGet KEYWORD2 57 | isIPv6Multicast KEYWORD2 58 | isLinkLocal KEYWORD2 59 | isLinkLocalAllNodes KEYWORD2 60 | isLinkLocalAllRouters KEYWORD2 61 | isMulticast KEYWORD2 62 | isOurAddress KEYWORD2 63 | isPost KEYWORD2 64 | isPut KEYWORD2 65 | isSolicitedNodeMulticastAddress KEYWORD2 66 | isValid KEYWORD2 67 | isZero KEYWORD2 68 | lastRoundTripTime KEYWORD2 69 | lastSequenceNumber KEYWORD2 70 | length KEYWORD2 71 | linkLocalAddress KEYWORD2 72 | localMac KEYWORD2 73 | localPort KEYWORD2 74 | lookupHostname KEYWORD2 75 | notFound KEYWORD2 76 | packet KEYWORD2 77 | packetDestination KEYWORD2 78 | packetDestinationPort KEYWORD2 79 | packetSource KEYWORD2 80 | packetSourcePort KEYWORD2 81 | path KEYWORD2 82 | payload KEYWORD2 83 | payloadEquals KEYWORD2 84 | payloadLength KEYWORD2 85 | prepareReply KEYWORD2 86 | prepareSend KEYWORD2 87 | print KEYWORD2 88 | printHeaders KEYWORD2 89 | printStatus KEYWORD2 90 | println KEYWORD2 91 | priority KEYWORD2 92 | protocol KEYWORD2 93 | readFrame KEYWORD2 94 | receivePacket KEYWORD2 95 | redirect KEYWORD2 96 | rejectPacket KEYWORD2 97 | remoteAddress KEYWORD2 98 | remotePort KEYWORD2 99 | routerMac KEYWORD2 100 | send KEYWORD2 101 | sendFrame KEYWORD2 102 | sendReply KEYWORD2 103 | sequenceNumber KEYWORD2 104 | setDestination KEYWORD2 105 | setDnsServerAddress KEYWORD2 106 | setEtherDestination KEYWORD2 107 | setEtherSource KEYWORD2 108 | setEui64 KEYWORD2 109 | setFacility KEYWORD2 110 | setGlobalAddress KEYWORD2 111 | setHopLimit KEYWORD2 112 | setIPv6Multicast KEYWORD2 113 | setIdentifier KEYWORD2 114 | setLinkLocalAllNodes KEYWORD2 115 | setLinkLocalAllRouters KEYWORD2 116 | setLinkLocalPrefix KEYWORD2 117 | setPayloadLength KEYWORD2 118 | setPriority KEYWORD2 119 | setProtocol KEYWORD2 120 | setRemoteAddress KEYWORD2 121 | setRouter KEYWORD2 122 | setSequenceNumber KEYWORD2 123 | setSolicitedNodeMulticastAddress KEYWORD2 124 | setSource KEYWORD2 125 | setZero KEYWORD2 126 | source KEYWORD2 127 | tcpSendRSTReply KEYWORD2 128 | timeLastRecieved KEYWORD2 129 | timeLastSent KEYWORD2 130 | transmitPayload KEYWORD2 131 | type KEYWORD2 132 | version KEYWORD2 133 | write KEYWORD2 134 | 135 | 136 | ####################################### 137 | # Constants (LITERAL1) 138 | ####################################### 139 | ETHERSIA_MAX_PACKET_SIZE LITERAL1 140 | ETHER_HEADER_LEN LITERAL1 141 | FlashStringMaker LITERAL1 142 | IP6_DEFAULT_HOP_LIMIT LITERAL1 143 | IP6_HEADER_LEN LITERAL1 144 | MAX_IPV6_ADDRESS_STR_LEN LITERAL1 145 | NEIGHBOUR_SOLICITATION_ATTEMPTS LITERAL1 146 | NEIGHBOUR_SOLICITATION_TIMEOUT LITERAL1 147 | ROUTER_SOLICITATION_ATTEMPTS LITERAL1 148 | ROUTER_SOLICITATION_TIMEOUT LITERAL1 149 | SysLogPortNumber LITERAL1 150 | TCP_HEADER_PTR LITERAL1 151 | TCP_MINIMUM_HEADER_LEN LITERAL1 152 | TCP_RECEIVE_HEADER_LEN LITERAL1 153 | TCP_TRANSMIT_HEADER_LEN LITERAL1 154 | TCP_WINDOW_SIZE LITERAL1 155 | TFTP_ACK_TIMEOUT LITERAL1 156 | TFTP_BLOCK_SIZE LITERAL1 157 | TFTP_DATA_TIMEOUT LITERAL1 158 | TFTP_RETRIES LITERAL1 159 | UDP_HEADER_LEN LITERAL1 160 | UDP_HEADER_PTR LITERAL1 161 | 162 | -------------------------------------------------------------------------------- /src/enc28j60.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Header file for direct Ethernet frame access to the ENC28J60 controller 3 | * @file enc28j60.h 4 | */ 5 | 6 | /* 7 | * Copyright (c) 2012-2013, Thingsquare, http://www.thingsquare.com/. 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 3. Neither the name of the copyright holder nor the names of its 19 | * contributors may be used to endorse or promote products derived 20 | * from this software without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 31 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 33 | * OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | */ 36 | 37 | #ifndef ENC28J60_H 38 | #define ENC28J60_H 39 | 40 | #include 41 | 42 | #include "EtherSia.h" 43 | 44 | /** 45 | * Send and receive Ethernet frames directly using a ENC28J60 controller. 46 | */ 47 | class EtherSia_ENC28J60 : public EtherSia { 48 | 49 | public: 50 | /** 51 | * Constructor that uses the default hardware SPI pins 52 | * @param cs the Arduino Chip Select / Slave Select pin (default 10 on Uno) 53 | */ 54 | EtherSia_ENC28J60(int8_t cs=SS); 55 | 56 | // Tell the compiler we want to use begin() from the base class 57 | using EtherSia::begin; 58 | 59 | /** 60 | * Initialise the Ethernet controller 61 | * Must be called before sending or receiving Ethernet frames 62 | * 63 | * @param address the local MAC address for the Ethernet interface 64 | * @return Returns true if setting up the Ethernet interface was successful 65 | */ 66 | boolean begin(const MACAddress &address); 67 | 68 | /** 69 | * Send an Ethernet frame 70 | * @param data a pointer to the data to send 71 | * @param datalen the length of the data in the packet 72 | * @return the number of bytes transmitted 73 | */ 74 | virtual uint16_t sendFrame(const uint8_t *data, uint16_t datalen); 75 | 76 | /** 77 | * Read an Ethernet frame 78 | * @param buffer a pointer to a buffer to write the packet to 79 | * @param bufsize the available space in the buffer 80 | * @return the length of the received packet 81 | * or 0 if no packet was received 82 | */ 83 | virtual uint16_t readFrame(uint8_t *buffer, uint16_t bufsize); 84 | 85 | private: 86 | 87 | uint8_t is_mac_mii_reg(uint8_t reg); 88 | uint8_t readreg(uint8_t reg); 89 | void writereg(uint8_t reg, uint8_t data); 90 | void setregbitfield(uint8_t reg, uint8_t mask); 91 | void clearregbitfield(uint8_t reg, uint8_t mask); 92 | void setregbank(uint8_t new_bank); 93 | void writedata(const uint8_t *data, int datalen); 94 | void writedatabyte(uint8_t byte); 95 | int readdata(uint8_t *buf, int len); 96 | uint8_t readdatabyte(void); 97 | void softreset(void); 98 | uint8_t readrev(void); 99 | void reset(void); 100 | 101 | void enc28j60_arch_spi_init(void); 102 | uint8_t enc28j60_arch_spi_write(uint8_t data); 103 | uint8_t enc28j60_arch_spi_read(void); 104 | void enc28j60_arch_spi_select(void); 105 | void enc28j60_arch_spi_deselect(void); 106 | 107 | // Previously defined in contiki/core/sys/clock.h 108 | void clock_delay_usec(uint16_t dt); 109 | 110 | uint8_t _bank; 111 | int8_t _cs; 112 | 113 | }; 114 | 115 | #endif /* ENC28J60_H */ 116 | -------------------------------------------------------------------------------- /src/HTTPServer.cpp: -------------------------------------------------------------------------------- 1 | #include "EtherSia.h" 2 | #include "util.h" 3 | 4 | FlashStringMaker(HTTPServer, typeHtml, "text/html"); 5 | FlashStringMaker(HTTPServer, typeCss, "text/css"); 6 | FlashStringMaker(HTTPServer, typePlain, "text/plain"); 7 | FlashStringMaker(HTTPServer, typeJson, "application/json"); 8 | 9 | FlashStringMaker(HTTPServer, status200, "200 OK"); 10 | FlashStringMaker(HTTPServer, status302, "302 Redirect"); 11 | FlashStringMaker(HTTPServer, status404, "404 Not Found"); 12 | 13 | const char PROGMEM HTTPServer::methodGet[] = "GET"; 14 | const char PROGMEM HTTPServer::methodPost[] = "POST"; 15 | const char PROGMEM HTTPServer::methodPut[] = "PUT"; 16 | const char PROGMEM HTTPServer::methodDelete[] = "DELETE"; 17 | 18 | 19 | 20 | HTTPServer::HTTPServer(EtherSia ðer, uint16_t localPort) : TCPServer(ether, localPort) 21 | { 22 | _bodyPtr = NULL; 23 | } 24 | 25 | void HTTPServer::printStatus(const __FlashStringHelper* status) 26 | { 27 | print(F("HTTP/1.0 ")); 28 | println(status); 29 | println(F("Server: EtherSia")); 30 | } 31 | 32 | void HTTPServer::printHeaders(const __FlashStringHelper* contentType, const __FlashStringHelper* status) 33 | { 34 | printStatus(status); 35 | print(F("Content-Type: ")); 36 | println(contentType); 37 | println(); 38 | } 39 | 40 | void HTTPServer::notFound() 41 | { 42 | if (havePacket()) { 43 | printHeaders(typePlain, status404); 44 | println(status404); 45 | sendReply(); 46 | } 47 | } 48 | 49 | void HTTPServer::redirect(const __FlashStringHelper* location) 50 | { 51 | printStatus(status302); 52 | print(F("Location: ")); 53 | println(location); 54 | println(); 55 | println(status302); 56 | sendReply(); 57 | } 58 | 59 | boolean HTTPServer::checkRequest(const char* method, const __FlashStringHelper* path) 60 | { 61 | char* payload = (char*)this->payload(); 62 | uint16_t length = payloadLength(); 63 | uint16_t endOfMethod; 64 | uint16_t pos; 65 | 66 | // Is there a TCP request ready for us? 67 | if (!havePacket()) 68 | return false; 69 | 70 | // Does the method match? 71 | endOfMethod = strlen_P(method); 72 | if (memcmp_P(payload, method, endOfMethod) != 0) { 73 | // Wrong method 74 | return false; 75 | } 76 | 77 | // Check there is a space after the method 78 | if (payload[endOfMethod] != ' ') { 79 | // No space after method 80 | return false; 81 | } 82 | 83 | // Do the paths match? 84 | const char *matchpath = reinterpret_cast(path); 85 | uint8_t maxlen = length - endOfMethod - 1; 86 | bool allowAny = false; 87 | _pathPtr = &payload[endOfMethod + 1]; 88 | for(uint8_t i=0; i < maxlen; i++) { 89 | char match = pgm_read_byte(matchpath + i); 90 | 91 | // Have we got the end of the path section in the HTTP request? 92 | if (isWhitespace(_pathPtr[i]) || _pathPtr[i] == '?') { 93 | if (match == '\0') { 94 | // Both ended at the same time 95 | _pathPtr[i] = '\0'; 96 | break; 97 | } else { 98 | // Path ended before the string we are trying to match against 99 | return false; 100 | } 101 | } 102 | 103 | // Allow anything after a '#' character 104 | if (match == '#') 105 | allowAny = true; 106 | 107 | // Allow any character to match a '?' 108 | if (match == '?' || allowAny) 109 | continue; 110 | 111 | // Do they match up? 112 | if (_pathPtr[i] != match) 113 | return false; 114 | } 115 | 116 | // Find the start of the body section 117 | _bodyPtr = NULL; 118 | for(pos = endOfMethod+1; pos < length-1; pos++) { 119 | if (payload[pos] == '\n' && (payload[pos-2] == '\n' || payload[pos-1] == '\n')) { 120 | // Store the location of the start of the body 121 | _bodyPtr = &payload[pos+1]; 122 | 123 | // For convenience NULL-terminate the body 124 | payload[length] = '\0'; 125 | break; 126 | } 127 | } 128 | 129 | return true; 130 | } 131 | 132 | uint16_t HTTPServer::bodyLength() 133 | { 134 | if (_bodyPtr == NULL) 135 | return 0; 136 | 137 | return payloadLength() - (_bodyPtr - (char*)payload()); 138 | } 139 | 140 | boolean HTTPServer::bodyEquals(const char str[]) 141 | { 142 | if (_bodyPtr == NULL) 143 | return false; 144 | 145 | return strcmp(_bodyPtr, str) == 0; 146 | } 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | EtherSia 2 | ======== 3 | 4 | A minimal IPv6 library for an [Arduino] with an [ENC28J60], [W5500] or [W5100] Ethernet controller. 5 | 6 | 7 | Features 8 | -------- 9 | - SLAAC (Neighbour Discovery Protocol / Stateless Auto-configuration) 10 | - HTTP Server 11 | - UDP Client and Server 12 | - DNS Client 13 | 14 | 15 | Design Decisions 16 | ---------------- 17 | 1. Optimised for Arduino - not a general purpose library 18 | 2. Should be easy to use - this is what Arduino is all about 19 | 3. Should work within the constraints of an [Uno] - 32k ROM and 2k RAM 20 | 4. Should follow the [Style Guide] and avoid using complex C features like Pointers and Callbacks 21 | 5. Decouple the core from protocols where possible, to allow for program size optimisations 22 | 6. Only use statically allocated memory to avoid leaks and keep memory usage down 23 | 24 | 25 | Limitations 26 | ----------- 27 | - Ethernet only 28 | - No DHCPv6 29 | - No Routing or RPL 30 | - No TCP Client 31 | - Stateless TCP in HTTP Server (single packet request/response) 32 | - No fragmentation support 33 | - A single local router on the network is assumed 34 | - The network prefix length is assumed to be /64 35 | 36 | If you need a more fully functional IPv6 stack, then take a look at [Contiki]. 37 | 38 | EtherSia is an IPv6 only library. If you are looking for an IPv4 library for [ENC28J60], 39 | then take a look at [EtherCard]. 40 | 41 | 42 | Compatibility 43 | ------------- 44 | 45 | EtherSia should work on any Arduino board with [ENC28J60], [W5500] or [W5100] based network interface. 46 | There are the results of boards I have tested with: 47 | 48 | | Board | Class | Tested? | CS Pin | Hardware MAC Address | 49 | |-------------------------------|------------------------|---------|--------|----------------------| 50 | | [Arduino Ethernet Shield] | [EtherSia_W5100] | Working | 10 | None | 51 | | [Arduino Ethernet Shield 2] | [EtherSia_W5500] | Working | 10 | None | 52 | | [Nanode v5] | [EtherSia_ENC28J60] | Working | 8 | UNI/O 11AA02E48 | 53 | | [Nanode RF] / Classic | [EtherSia_ENC28J60] | - | 8 | MCP79411 | 54 | | [Nano Shield] | [EtherSia_ENC28J60] | Working | 10 | None | 55 | | Velleman [KA04]/[VMA04] | [EtherSia_ENC28J60] | - | 10 | None | 56 | | [Ciseco Ethernet Shield] K016 | [EtherSia_ENC28J60] | - | 10 | None | 57 | | [Snootlab Gate 0.5] | [EtherSia_ENC28J60] | - | 10 | None | 58 | | _Testing on Linux_ | [EtherSia_LinuxSocket] | Working | - | - | 59 | 60 | License: [3-clause BSD license] 61 | 62 | 63 | [Arduino]: http://www.arduino.cc/ 64 | [Uno]: http://www.arduino.cc/en/Main/ArduinoBoardUno 65 | [Style Guide]: http://www.arduino.cc/en/Reference/APIStyleGuide 66 | [Contiki]: http://www.contiki-os.org/ 67 | [ENC28J60]: http://www.microchip.com/ENC28J60 68 | [W5500]: http://www.wiznet.io/product-item/w5500/ 69 | [W5100]: http://www.wiznet.io/product-item/w5100/ 70 | [EtherCard]: http://github.com/njh/EtherCard 71 | [3-clause BSD license]: http://opensource.org/licenses/BSD-3-Clause 72 | 73 | [EtherSia_ENC28J60]: http://www.aelius.com/njh/ethersia/class_ether_sia___e_n_c28_j60.html 74 | [EtherSia_LinuxSocket]: http://www.aelius.com/njh/ethersia/class_ether_sia___linux_socket.html 75 | [EtherSia_W5100]: http://www.aelius.com/njh/ethersia/class_ether_sia___w5100.html 76 | [EtherSia_W5500]: http://www.aelius.com/njh/ethersia/class_ether_sia___w5500.html 77 | 78 | [Arduino Ethernet Shield]: https://www.arduino.cc/en/Main/ArduinoEthernetShield 79 | [Arduino Ethernet Shield 2]: http://www.arduino.org/products/shields/arduino-ethernet-shield-2 80 | [Nanode v5]: https://wiki.london.hackspace.org.uk/view/Project:Nanode 81 | [Nanode RF]: http://ichilton.github.com/nanode/rf/build_guide.html 82 | [Nano Shield]: http://www.tweaking4all.com/hardware/arduino/arduino-enc28j60-ethernet/ 83 | [KA04]: http://www.vellemanprojects.eu/products/view/?id=412244 84 | [VMA04]: http://www.vellemanprojects.eu/products/view/?id=412540 85 | [Ciseco Ethernet Shield]: http://openmicros.org/articles/88-ciseco-product-documentation/178-enc28j60-ethernet-shield-how-to-build 86 | [Snootlab Gate 0.5]: http://shop.snootlab.com/ethernet/85-gate.html 87 | -------------------------------------------------------------------------------- /src/Socket.cpp: -------------------------------------------------------------------------------- 1 | #include "EtherSia.h" 2 | #include "util.h" 3 | 4 | Socket::Socket(EtherSia ðer) : Socket(ether, (uint16_t)0) 5 | { 6 | } 7 | 8 | Socket::Socket(EtherSia ðer, uint16_t localPort) : _ether(ether) 9 | { 10 | _localPort = localPort; 11 | _remoteAddress.setZero(); 12 | _remotePort = 0; 13 | _writePos = -1; 14 | } 15 | 16 | boolean Socket::setRemoteAddress(const __FlashStringHelper* remoteAddress, uint16_t remotePort) 17 | { 18 | char temp[64]; 19 | 20 | // Copy out of Flash Memory 21 | strncpy_P(temp, (const char*)remoteAddress, sizeof(temp)-1); 22 | 23 | return setRemoteAddress(temp, remotePort); 24 | } 25 | 26 | boolean Socket::setRemoteAddress(const char *remoteAddress, uint16_t remotePort) 27 | { 28 | if (containsColon(remoteAddress)) { 29 | // Parse a human readable IPv6 Address string 30 | if (!_remoteAddress.fromString(remoteAddress)) { 31 | return false; 32 | } 33 | } else { 34 | // Lookup a hostname 35 | IPv6Address *address = _ether.lookupHostname(remoteAddress); 36 | if (address) { 37 | _remoteAddress = *address; 38 | } else { 39 | // Fail 40 | return false; 41 | } 42 | } 43 | 44 | return setRemoteAddress(_remoteAddress, remotePort); 45 | } 46 | 47 | boolean Socket::setRemoteAddress(IPv6Address &remoteAddress, uint16_t remotePort) 48 | { 49 | _remotePort = remotePort; 50 | _remoteAddress = remoteAddress; 51 | 52 | if (_localPort == 0) { 53 | _localPort = random(20000, 30000); 54 | } 55 | 56 | // Work out the MAC address to use 57 | if (_ether.inOurSubnet(_remoteAddress)) { 58 | MACAddress *mac = _ether.discoverNeighbour(_remoteAddress); 59 | if (mac == NULL) { 60 | return false; 61 | } else { 62 | _remoteMac = *mac; 63 | } 64 | } else { 65 | _remoteMac = _ether.routerMac(); 66 | } 67 | 68 | return true; 69 | } 70 | 71 | IPv6Address& Socket::remoteAddress() 72 | { 73 | return _remoteAddress; 74 | } 75 | 76 | uint16_t Socket::remotePort() 77 | { 78 | return _remotePort; 79 | } 80 | 81 | uint16_t Socket::localPort() 82 | { 83 | return _localPort; 84 | } 85 | 86 | IPv6Address& Socket::packetSource() 87 | { 88 | return _ether.packet().source(); 89 | } 90 | 91 | IPv6Address& Socket::packetDestination() 92 | { 93 | return _ether.packet().destination(); 94 | } 95 | 96 | void Socket::send(boolean isReply) 97 | { 98 | if (_writePos > 0) { 99 | send(_writePos, isReply); 100 | _writePos = -1; 101 | } 102 | } 103 | 104 | void Socket::send(const char *data, boolean isReply) 105 | { 106 | send((const uint8_t *)data, strlen(data), isReply); 107 | } 108 | 109 | void Socket::send(const void *data, uint16_t length, boolean isReply) 110 | { 111 | uint8_t* payload = this->transmitPayload(); 112 | 113 | // FIXME: check it isn't too big 114 | memcpy(payload, data, length); 115 | 116 | send(length, isReply); 117 | } 118 | 119 | void Socket::send(uint16_t length, boolean isReply) 120 | { 121 | IPv6Packet& packet = _ether.packet(); 122 | 123 | if (isReply) { 124 | _ether.prepareReply(); 125 | } else { 126 | packet.setDestination(_remoteAddress); 127 | packet.setEtherDestination(_remoteMac); 128 | _ether.prepareSend(); 129 | } 130 | 131 | sendInternal(length, isReply); 132 | } 133 | 134 | void Socket::sendReply() { 135 | send(true); 136 | } 137 | 138 | void Socket::sendReply(uint16_t length) { 139 | send(length, true); 140 | } 141 | 142 | void Socket::sendReply(const char *data) { 143 | send(data, true); 144 | } 145 | 146 | void Socket::sendReply(const void *data, uint16_t length) { 147 | send(data, length, true); 148 | } 149 | 150 | boolean Socket::payloadEquals(const char *str) 151 | { 152 | return strncmp((char*)payload(), str, payloadLength()) == 0; 153 | } 154 | 155 | uint8_t* Socket::transmitPayload() 156 | { 157 | // This method can be overloaded 158 | // by default return pointer to the received payload 159 | return this->payload(); 160 | } 161 | 162 | boolean Socket::handleWriteNewline() 163 | { 164 | return true; 165 | } 166 | 167 | void Socket::writePayloadHeader() 168 | { 169 | } 170 | 171 | size_t Socket::write(uint8_t chr) 172 | { 173 | uint8_t *payload = this->transmitPayload(); 174 | boolean doWriteChar = true; 175 | 176 | if (chr == '\n' || chr == '\r') { 177 | doWriteChar = handleWriteNewline(); 178 | } else if (_writePos == -1) { 179 | _writePos = 0; 180 | writePayloadHeader(); 181 | } 182 | 183 | if (doWriteChar) { 184 | payload[_writePos++] = chr; 185 | return 1; 186 | } else { 187 | return 0; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/dummy.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Nicholas Humfrey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of its 14 | * contributors may be used to endorse or promote products derived 15 | * from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 28 | * OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | */ 31 | 32 | #if !defined(ARDUINO) 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "dummy.h" 42 | 43 | 44 | EtherSia_Dummy::EtherSia_Dummy() 45 | { 46 | for(size_t i=0; ilength < bufsize) { 93 | memcpy(buffer, frame->packet, frame->length); 94 | return frame->length; 95 | } else { 96 | // Packet is too big for EtherSia buffer 97 | return 0; 98 | } 99 | } else { 100 | // Tried to read packet but none available 101 | return 0; 102 | } 103 | } 104 | 105 | 106 | frame_t& 107 | EtherSia_Dummy::getSent(size_t pos) 108 | { 109 | return _sent[pos]; 110 | } 111 | 112 | 113 | frame_t& 114 | EtherSia_Dummy::getLastSent() 115 | { 116 | return _sent[_sentCount-1]; 117 | } 118 | 119 | 120 | void 121 | EtherSia_Dummy::end() 122 | { 123 | clearSent(); 124 | clearRecieved(); 125 | } 126 | 127 | void EtherSia_Dummy::clearSent() 128 | { 129 | for(size_t i=0; i 3 | #include 4 | #include 5 | 6 | 7 | 8 | /** 9 | * Custom TFTP server class 10 | * 11 | * Supports the following filenames: 12 | * serial: serial port (write only) 13 | * eeprom: the built-in EEPROM (read/write) 14 | * i2c: an external AT24C128 EEPROM connected to i2c 15 | * 16 | */ 17 | class CustomTFTPServer: public TFTPServer { 18 | 19 | const uint16_t AT24C128_LENGTH = 16384; 20 | const uint8_t AT24C128_ADDRESS = 0x50; 21 | const uint8_t AT24C128_BUF_SIZE = 32; // This is the size of the Arduino buffer 22 | 23 | public: 24 | /** 25 | * Construct a new custom TFTP server 26 | * 27 | * @param ether The Ethernet interface to attach the TFTP server to 28 | */ 29 | CustomTFTPServer(EtherSia ðer) : TFTPServer(ether) {}; 30 | 31 | int8_t openFile(const char* filename) 32 | { 33 | if (strcmp(filename, "serial") == 0) { 34 | // Serial port is File Number: 1 35 | return 1; 36 | } else if (strcmp(filename, "eeprom") == 0) { 37 | // Internal EEPROM is File Number: 2 38 | return 2; 39 | } else if (strcmp(filename, "i2c") == 0) { 40 | // External i2c EEPROM is File Number: 3 41 | return 3; 42 | } else { 43 | // File Not Found 44 | return -1; 45 | } 46 | } 47 | 48 | void writeBytes(int8_t fileno, uint16_t block, const uint8_t* data, uint16_t len) 49 | { 50 | uint16_t offset = (block-1) * TFTP_BLOCK_SIZE; 51 | 52 | if (fileno == 1) { 53 | // Write straight to serial port 54 | Serial.write(data, len); 55 | } else if (fileno == 2) { 56 | // Handle writing to internal EEPROM 57 | for(uint16_t i=0; i TFTP_BLOCK_SIZE) 77 | len = TFTP_BLOCK_SIZE; 78 | for(uint16_t i=0; i= AT24C128_LENGTH) 100 | return; 101 | 102 | Wire.beginTransmission(AT24C128_ADDRESS); 103 | Wire.write((int)(eeaddress >> 8)); // MSB 104 | Wire.write((int)(eeaddress & 0xFF)); // LSB 105 | 106 | // WARNING: this page write only works because the TFTP block size (512) 107 | // is divisible by the EEPROM page size (64 bytes) 108 | // But it must also fit within the Arduino 32 byte Wire buffer 109 | uint16_t end = i + (AT24C128_BUF_SIZE/2); 110 | while(i TFTP_BLOCK_SIZE) 124 | len = TFTP_BLOCK_SIZE; 125 | 126 | for(uint16_t i=0; i> 8)); // MSB 130 | Wire.write((int)(eeaddress & 0xFF)); // LSB 131 | Wire.endTransmission(); 132 | 133 | Wire.requestFrom(AT24C128_ADDRESS, AT24C128_BUF_SIZE); 134 | 135 | uint16_t end = i + AT24C128_BUF_SIZE; 136 | while(i 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | 50 | #include "LinuxSocket.h" 51 | 52 | 53 | EtherSia_LinuxSocket::EtherSia_LinuxSocket(const char* ifname) 54 | { 55 | strncpy(this->ifname, ifname, sizeof(this->ifname)-1); 56 | ifindex = -1; 57 | sockfd = -1; 58 | } 59 | 60 | 61 | boolean 62 | EtherSia_LinuxSocket::begin(const MACAddress &address) 63 | { 64 | _localMac = address; 65 | 66 | ifindex = if_nametoindex(ifname); 67 | if (ifindex <= 0) { 68 | perror("if_nametoindex"); 69 | return false; 70 | } 71 | 72 | if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETHER_TYPE_IPV6))) == -1) { 73 | perror("socket(PF_PACKET)"); 74 | return -1; 75 | } 76 | 77 | /* Set non-blocking mode */ 78 | fcntl(sockfd, F_SETFL, O_NONBLOCK); 79 | 80 | /* Set interface to promiscuous mode */ 81 | struct ifreq ifopts; 82 | int sockopt = 0; 83 | strncpy(ifopts.ifr_name, ifname, IFNAMSIZ-1); 84 | ioctl(sockfd, SIOCGIFFLAGS, &ifopts); 85 | ifopts.ifr_flags |= IFF_PROMISC; 86 | ioctl(sockfd, SIOCSIFFLAGS, &ifopts); 87 | 88 | /* Allow the socket to be reused */ 89 | if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof sockopt) == -1) { 90 | perror("setsockopt(SO_REUSEADDR)"); 91 | close(sockfd); 92 | return false; 93 | } 94 | 95 | /* Bind to device */ 96 | if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, ifname, IFNAMSIZ-1) == -1) { 97 | perror("setsockopt(SO_BINDTODEVICE)"); 98 | close(sockfd); 99 | return false; 100 | } 101 | 102 | return EtherSia::begin(); 103 | } 104 | 105 | /*---------------------------------------------------------------------------*/ 106 | 107 | uint16_t 108 | EtherSia_LinuxSocket::sendFrame(const uint8_t *data, uint16_t datalen) 109 | { 110 | struct sockaddr_ll socket_address; 111 | 112 | /* Index of the network device */ 113 | socket_address.sll_ifindex = ifindex; 114 | 115 | /* Address length*/ 116 | socket_address.sll_halen = ETH_ALEN; 117 | 118 | /* Destination MAC */ 119 | IPv6Packet *packet = (IPv6Packet*)data; 120 | memcpy(&socket_address.sll_addr, packet->etherDestination(), 6); 121 | 122 | /* Send packet */ 123 | int result = sendto(sockfd, data, datalen, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)); 124 | if (result <= 0) { 125 | perror("sendto"); 126 | return 0; 127 | } 128 | 129 | return result; 130 | } 131 | 132 | /*---------------------------------------------------------------------------*/ 133 | 134 | uint16_t 135 | EtherSia_LinuxSocket::readFrame(uint8_t *buffer, uint16_t bufsize) 136 | { 137 | int result = read(sockfd, buffer, bufsize); 138 | if (result <= 0) { 139 | if (errno != EAGAIN) 140 | perror("Failed to read"); 141 | return 0; 142 | } 143 | 144 | return result; 145 | } 146 | 147 | void 148 | EtherSia_LinuxSocket::end() 149 | { 150 | if (sockfd > 0) { 151 | close(sockfd); 152 | sockfd = -1; 153 | } 154 | } 155 | 156 | #endif 157 | --------------------------------------------------------------------------------