diff --git a/src/EtherCard.h b/src/EtherCard.h index 4f2ee6e..d6373cf 100644 --- a/src/EtherCard.h +++ b/src/EtherCard.h @@ -105,6 +105,7 @@ typedef void (*DhcpOptionCallback)( const byte* data, ///< DHCP option data uint8_t len); ///< Length of the DHCP option data +typedef void (*IcmpCallback)(const uint8_t *src_ip); /** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use. @@ -298,7 +299,7 @@ class EtherCard : public Ethernet { /** @brief Resister the function to handle ping events * @param cb Pointer to function */ - static void registerPingCallback (void (*cb)(uint8_t*)); + static void registerPingCallback (const IcmpCallback cb); /** @brief Send ping * @param destip Pointer to 4 byte destination IP address @@ -357,7 +358,7 @@ class EtherCard : public Ethernet { * @param len Not used * @return bool True if packet processed */ - static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop + static bool udpServerHasProcessedPacket(const IpHeader &ip, const uint8_t *iter, const uint8_t *last); //called by tcpip, in packetLoop // dhcp.cpp /** @brief Update DHCP state diff --git a/src/net.h b/src/net.h index 6b585d8..19bc435 100755 --- a/src/net.h +++ b/src/net.h @@ -39,13 +39,29 @@ #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define HTONS(x) BSWAP_16(x) #define HTONL(x) BSWAP_32(x) + #define NTOHS(x) BSWAP_16(x) + #define NTOHL(x) BSWAP_32(x) #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define HTONS(x) x #define HTONL(x) x + #define NTOHS(x) x + #define NTOHL(x) x #else # error __BYTE_ORDER__ not defined! PLease define it for your platform #endif +inline uint16_t htons(const uint16_t v) +{ return HTONS(v); } + +inline uint32_t htonl(const uint32_t v) +{ return HTONL(v); } + +inline uint16_t ntohs(const uint16_t v) +{ return NTOHS(v); } + +inline uint32_t ntohl(const uint32_t v) +{ return NTOHL(v); } + // ******* SERVICE PORTS ******* #define HTTP_PORT 80 #define DNS_PORT 53 @@ -56,8 +72,7 @@ #define ETH_LEN 6 // values of certain bytes: #define ETHTYPE_ARP_V HTONS(0x0806) -#define ETHTYPE_IP_H_V 0x08 -#define ETHTYPE_IP_L_V 0x00 +#define ETHTYPE_IP_V HTONS(0x0800) // byte positions in the ethernet frame: // // Ethernet type field (2bytes): @@ -78,20 +93,15 @@ struct EthHeader // ******* IP ******* -#define IP_HEADER_LEN 20 #define IP_LEN 4 -// ip.src -#define IP_SRC_P 0x1a -#define IP_DST_P 0x1e -#define IP_HEADER_LEN_VER_P 0xe -#define IP_CHECKSUM_P 0x18 -#define IP_TTL_P 0x16 -#define IP_FLAGS_P 0x14 -#define IP_P 0xe -#define IP_TOTLEN_H_P 0x10 -#define IP_TOTLEN_L_P 0x11 - -#define IP_PROTO_P 0x17 +#define IP_V4 0x4 +#define IP_IHL 0x5 + +enum IpFlags +{ + IP_DF = 1 << 1, + IP_MF = 1 << 0, +}; #define IP_PROTO_ICMP_V 1 #define IP_PROTO_TCP_V 6 @@ -100,20 +110,46 @@ struct EthHeader struct IpHeader { - uint8_t version:4; - uint8_t ihl:4; - uint8_t dscp:6; - uint8_t ecn:2; + uint8_t versionIhl; + uint8_t dscpEcn; uint16_t totalLen; uint16_t identification; - uint8_t flags:3; - uint16_t fragmentOffset:13; + uint16_t flagsFragmentOffset; uint8_t ttl; uint8_t protocol; uint16_t hchecksum; uint8_t spaddr[IP_LEN]; uint8_t tpaddr[IP_LEN]; -}; + + static uint8_t version_mask() { return 0xF0; } + uint8_t version() const { return (versionIhl & version_mask()) >> 4; } + void version(const uint8_t v) + { + versionIhl &= ~version_mask(); + versionIhl |= (v << 4) & version_mask(); + } + + uint8_t ihl() const { return versionIhl & 0xF; } + void ihl(const uint8_t i) + { + versionIhl &= version_mask(); + versionIhl |= i & ~version_mask(); + } + + static uint16_t flags_mask() { return HTONS(0xE000); } + + void flags(const uint16_t f) + { + flagsFragmentOffset &= ~flags_mask(); + flagsFragmentOffset |= HTONS(f << 13) & flags_mask(); + } + void fragmentOffset(const uint16_t o) + { + flagsFragmentOffset &= flags_mask(); + flagsFragmentOffset |= HTONS(o) & ~flags_mask(); + } + +} __attribute__((__packed__)); // ******* ARP ******* diff --git a/src/tcpip.cpp b/src/tcpip.cpp index bc5f2e2..ab9136d 100644 --- a/src/tcpip.cpp +++ b/src/tcpip.cpp @@ -48,7 +48,7 @@ static const char *client_postval; static const char *client_urlbuf; // Pointer to c-string path part of HTTP request URL static const char *client_urlbuf_var; // Pointer to c-string filename part of HTTP request URL static const char *client_hoststr; // Pointer to c-string hostname of current HTTP request -static void (*icmp_cb)(uint8_t *ip); // Pointer to callback function for ICMP ECHO response handler (triggers when localhost receives ping response (pong)) +static IcmpCallback icmp_cb; // Pointer to callback function for ICMP ECHO response handler (triggers when localhost receives ping response (pong)) static uint16_t info_data_len; // Length of TCP/IP payload static uint8_t seqnum = 0xa; // My initial tcp sequence number @@ -59,12 +59,59 @@ static unsigned long SEQ; // TCP/IP sequence number #define CLIENTMSS 550 #define TCP_DATA_START ((uint16_t)TCP_SRC_PORT_H_P+(gPB[TCP_HEADER_LEN_P]>>4)*4) // Get offset of TCP/IP payload data -const unsigned char iphdr[] PROGMEM = { 0x45,0,0,0x82,0,0,0x40,0,0x20 }; //IP header const unsigned char ntpreqhdr[] PROGMEM = { 0xE3,0,4,0xFA,0,1,0,0,0,1 }; //NTP request header extern const uint8_t allOnes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // Used for hardware (MAC) and IP broadcast addresses -static void fill_checksum(uint8_t dest, uint8_t off, uint16_t len,uint8_t type) { - const uint8_t* ptr = gPB + off; +// static void log_data(const char *message, const uint8_t *ptr, uint16_t len) +// { +// Serial.print(message); +// Serial.println(':'); +// for (const uint8_t *last = ptr + len; ptr != last; ++ptr) +// { +// Serial.print(*ptr, HEX); +// Serial.print(' '); +// } +// Serial.println(); +// } + +static inline EthHeader ðernet_header() +{ + uint8_t *iter = gPB; // avoid strict aliasing warning + return *(EthHeader *)iter; +} + +static inline uint8_t *ethernet_payload() +{ + return (uint8_t *)ðernet_header() + sizeof(EthHeader); +} + +static inline ArpHeader &arp_header() +{ + return *(ArpHeader *)ethernet_payload(); +} + +static inline IpHeader &ip_header() +{ + return *(IpHeader *)ethernet_payload(); +} + +static inline uint8_t *ip_payload() +{ + return (uint8_t *)&ip_header() + sizeof(IpHeader); +} + +static inline uint8_t *tcp_header() +{ + return (uint8_t *)ip_payload(); +} + +static inline uint8_t *udp_header() +{ + return (uint8_t *)ip_payload(); +} + + +static void fill_checksum(uint16_t &checksum, const uint8_t *ptr, uint16_t len, uint8_t type) { uint32_t sum = type==1 ? IP_PROTO_UDP_V+len-8 : type==2 ? IP_PROTO_TCP_V+len-8 : 0; while(len >1) { @@ -77,26 +124,32 @@ static void fill_checksum(uint8_t dest, uint8_t off, uint16_t len,uint8_t type) while (sum>>16) sum = (uint16_t) sum + (sum >> 16); uint16_t ck = ~ (uint16_t) sum; - gPB[dest] = ck>>8; - gPB[dest+1] = ck; + checksum = HTONS(ck); +} + +static void fill_checksum(uint8_t dest, uint8_t off, uint16_t len, uint8_t type) { + uint8_t *iter = gPB; + const uint8_t* ptr = iter + off; + uint16_t &checksum = *(uint16_t *)(iter + dest); + fill_checksum(checksum, ptr, len, type); } static boolean is_lan(const uint8_t source[IP_LEN], const uint8_t destination[IP_LEN]); -static void init_eth_header(EthHeader &h, const uint8_t *thaddr) +static void init_eth_header(const uint8_t *thaddr) { + EthHeader &h = ethernet_header(); EtherCard::copyMac(h.thaddr, thaddr); EtherCard::copyMac(h.shaddr, EtherCard::mymac); } static void init_eth_header( - EthHeader &h, const uint8_t *thaddr, const uint16_t etype ) { - init_eth_header(h, thaddr); - h.etype = etype; + init_eth_header(thaddr); + ethernet_header().etype = etype; } // check if ARP request is ongoing @@ -119,29 +172,34 @@ static bool client_arp_ready(const uint8_t *ip) // - broadcast MAC address if none are found static const uint8_t *client_arp_get(const uint8_t *ip) { - const uint8_t *mac = EtherCard::arpStoreGetMac(is_lan(EtherCard::myip, ip) ? ip : EtherCard::gwip); - if (mac) - return mac; - return allOnes; -} - -static void setMACs (const uint8_t *mac) { - EtherCard::copyMac(gPB + ETH_DST_MAC, mac); - EtherCard::copyMac(gPB + ETH_SRC_MAC, EtherCard::mymac); -} + // see http://tldp.org/HOWTO/Multicast-HOWTO-2.html + // multicast or broadcast address, https://github.com/njh/EtherCard/issues/59 + const uint8_t *mac; + if ( + (ip[0] & 0xF0) == 0xE0 + || *((uint32_t *) ip) == 0xFFFFFFFF + || !memcmp(EtherCard::broadcastip, ip, IP_LEN) + || (mac = EtherCard::arpStoreGetMac(is_lan(EtherCard::myip, ip) ? ip : EtherCard::gwip)) == NULL + ) + return allOnes; -static void setMACandIPs (const uint8_t *mac, const uint8_t *dst) { - setMACs(mac); - EtherCard::copyIp(gPB + IP_DST_P, dst); - EtherCard::copyIp(gPB + IP_SRC_P, EtherCard::myip); + return mac; } -static void setMACandIPs (const uint8_t *dst) { - setMACandIPs(client_arp_get(dst), dst); +static void init_ip_header( + const uint8_t *dst + ) +{ + IpHeader &iph = ip_header(); + EtherCard::copyIp(iph.tpaddr, dst); + EtherCard::copyIp(iph.spaddr, EtherCard::myip); + iph.flags(IP_DF); + iph.fragmentOffset(0); + iph.ttl = 64; } -static uint8_t check_ip_message_is_from(const uint8_t *ip) { - return memcmp(gPB + IP_SRC_P, ip, IP_LEN) == 0; +static uint8_t check_ip_message_is_from(const IpHeader &iph, const uint8_t *ip) { + return memcmp(iph.spaddr, ip, IP_LEN) == 0; } static boolean is_lan(const uint8_t source[IP_LEN], const uint8_t destination[IP_LEN]) { @@ -155,30 +213,28 @@ static boolean is_lan(const uint8_t source[IP_LEN], const uint8_t destination[IP return true; } -static uint8_t eth_type_is_ip_and_my_ip(uint16_t len) { - return len >= 42 && gPB[ETH_TYPE_H_P] == ETHTYPE_IP_H_V && - gPB[ETH_TYPE_L_P] == ETHTYPE_IP_L_V && - gPB[IP_HEADER_LEN_VER_P] == 0x45 && - (memcmp(gPB + IP_DST_P, EtherCard::myip, IP_LEN) == 0 //not my IP - || (memcmp(gPB + IP_DST_P, EtherCard::broadcastip, IP_LEN) == 0) //not subnet broadcast - || (memcmp(gPB + IP_DST_P, allOnes, IP_LEN) == 0)); //not global broadcasts +static uint8_t is_my_ip(const IpHeader &iph) { + return iph.version() == IP_V4 && iph.ihl() == IP_IHL && + (memcmp(iph.tpaddr, EtherCard::myip, IP_LEN) == 0 //not my IP + || (memcmp(iph.tpaddr, EtherCard::broadcastip, IP_LEN) == 0) //not subnet broadcast + || (memcmp(iph.tpaddr, allOnes, IP_LEN) == 0)); //not global broadcasts //!@todo Handle multicast } -static void fill_ip_hdr_checksum() { - gPB[IP_CHECKSUM_P] = 0; - gPB[IP_CHECKSUM_P+1] = 0; - gPB[IP_FLAGS_P] = 0x40; // don't fragment - gPB[IP_FLAGS_P+1] = 0; // fragment offset - gPB[IP_TTL_P] = 64; // ttl - fill_checksum(IP_CHECKSUM_P, IP_P, IP_HEADER_LEN,0); +static void fill_ip_hdr_checksum(IpHeader &iph) { + iph.hchecksum = 0; + fill_checksum(iph.hchecksum, (const uint8_t *)&iph, sizeof(IpHeader), 0); } -static void make_eth_ip() { - setMACs(gPB + ETH_SRC_MAC); - EtherCard::copyIp(gPB + IP_DST_P, gPB + IP_SRC_P); - EtherCard::copyIp(gPB + IP_SRC_P, EtherCard::myip); - fill_ip_hdr_checksum(); +static void make_eth_ip_reply(const uint16_t payloadlen = 0xFFFF) { + init_eth_header(ethernet_header().shaddr); + init_ip_header(ip_header().spaddr); + + IpHeader &iph = ip_header(); + if (payloadlen != 0xFFFF) + iph.totalLen = htons(ip_payload() - (uint8_t *)&iph + payloadlen); + + fill_ip_hdr_checksum(iph); } static void step_seq(uint16_t rel_ack_num,uint8_t cp_seq) { @@ -212,17 +268,11 @@ static void make_tcphead(uint16_t rel_ack_num,uint8_t cp_seq) { } static void make_arp_answer_from_request() { - uint8_t *iter = gPB; - EthHeader &eh = *(EthHeader *)(iter); - iter += sizeof(EthHeader); - - ArpHeader &arp = *(ArpHeader *)(iter); - iter += sizeof(ArpHeader); - // set ethernet layer mac addresses - init_eth_header(eh, arp.shaddr); + init_eth_header(arp_header().shaddr); // set ARP answer from the request buffer + ArpHeader &arp = arp_header(); arp.opcode = ETH_ARP_OPCODE_REPLY; EtherCard::copyMac(arp.thaddr, arp.shaddr); EtherCard::copyMac(arp.shaddr, EtherCard::mymac); @@ -230,11 +280,11 @@ static void make_arp_answer_from_request() { EtherCard::copyIp(arp.spaddr, EtherCard::myip); // send ethernet frame - EtherCard::packetSend(iter - gPB); // 42 + EtherCard::packetSend((uint8_t *)&arp + sizeof(ArpHeader) - gPB); // 42 } static void make_echo_reply_from_request(uint16_t len) { - make_eth_ip(); + make_eth_ip_reply(); gPB[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V; if (gPB[ICMP_CHECKSUM_P] > (0xFF-0x08)) gPB[ICMP_CHECKSUM_P+1]++; @@ -245,9 +295,9 @@ static void make_echo_reply_from_request(uint16_t len) { void EtherCard::makeUdpReply (const char *data,uint8_t datalen,uint16_t port) { if (datalen>220) datalen = 220; - gPB[IP_TOTLEN_H_P] = (IP_HEADER_LEN+UDP_HEADER_LEN+datalen) >>8; - gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+UDP_HEADER_LEN+datalen; - make_eth_ip(); + + make_eth_ip_reply(UDP_HEADER_LEN + datalen); + IpHeader &iph = ip_header(); gPB[UDP_DST_PORT_H_P] = gPB[UDP_SRC_PORT_H_P]; gPB[UDP_DST_PORT_L_P] = gPB[UDP_SRC_PORT_L_P]; gPB[UDP_SRC_PORT_H_P] = port>>8; @@ -257,14 +307,12 @@ void EtherCard::makeUdpReply (const char *data,uint8_t datalen,uint16_t port) { gPB[UDP_CHECKSUM_H_P] = 0; gPB[UDP_CHECKSUM_L_P] = 0; memcpy(gPB + UDP_DATA_P, data, datalen); - fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + datalen,1); - packetSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen); + fill_checksum(UDP_CHECKSUM_H_P, (uint8_t *)&iph.spaddr - gPB, 16 + datalen,1); + packetSend(udp_header() - gPB + UDP_HEADER_LEN + datalen); } static void make_tcp_synack_from_syn() { - gPB[IP_TOTLEN_H_P] = 0; - gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4; - make_eth_ip(); + make_eth_ip_reply(TCP_HEADER_LEN_PLAIN+4); gPB[TCP_FLAGS_P] = TCP_FLAGS_SYNACK_V; make_tcphead(1,0); gPB[TCP_SEQ_H_P+0] = 0; @@ -279,13 +327,12 @@ static void make_tcp_synack_from_syn() { gPB[TCP_HEADER_LEN_P] = 0x60; gPB[TCP_WIN_SIZE] = 0x5; // 1400=0x578 gPB[TCP_WIN_SIZE+1] = 0x78; - fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN+4,2); - EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4+ETH_HEADER_LEN); + fill_checksum(TCP_CHECKSUM_H_P, (uint8_t *)&ip_header().spaddr - gPB, 8+TCP_HEADER_LEN_PLAIN+4,2); + EtherCard::packetSend(tcp_header() - gPB + TCP_HEADER_LEN_PLAIN+4); } uint16_t EtherCard::getTcpPayloadLength() { - int16_t i = (((int16_t)gPB[IP_TOTLEN_H_P])<<8)|gPB[IP_TOTLEN_L_P]; - i -= IP_HEADER_LEN; + int16_t i = ntohs(ip_header().totalLen) - sizeof(IpHeader); i -= (gPB[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes; if (i<=0) i = 0; @@ -297,25 +344,21 @@ static void make_tcp_ack_from_any(int16_t datlentoack,uint8_t addflags) { if (addflags!=TCP_FLAGS_RST_V && datlentoack==0) datlentoack = 1; make_tcphead(datlentoack,1); // no options - uint16_t j = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN; - gPB[IP_TOTLEN_H_P] = j>>8; - gPB[IP_TOTLEN_L_P] = j; - make_eth_ip(); + make_eth_ip_reply(TCP_HEADER_LEN_PLAIN); gPB[TCP_WIN_SIZE] = 0x4; // 1024=0x400, 1280=0x500 2048=0x800 768=0x300 gPB[TCP_WIN_SIZE+1] = 0; - fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN,2); - EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN); + fill_checksum(TCP_CHECKSUM_H_P, (uint8_t *)&ip_header().spaddr - gPB, 8+TCP_HEADER_LEN_PLAIN,2); + EtherCard::packetSend(tcp_header() - gPB + TCP_HEADER_LEN_PLAIN); } static void make_tcp_ack_with_data_noflags(uint16_t dlen) { - uint16_t j = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen; - gPB[IP_TOTLEN_H_P] = j>>8; - gPB[IP_TOTLEN_L_P] = j; - fill_ip_hdr_checksum(); + IpHeader &iph = ip_header(); + iph.totalLen = htons(ip_payload() - (uint8_t *)&ip_header() + TCP_HEADER_LEN_PLAIN + dlen); + fill_ip_hdr_checksum(iph); gPB[TCP_CHECKSUM_H_P] = 0; gPB[TCP_CHECKSUM_L_P] = 0; - fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN+dlen,2); - EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN); + fill_checksum(TCP_CHECKSUM_H_P, (uint8_t *)&ip_header().spaddr - gPB, 8+TCP_HEADER_LEN_PLAIN+dlen,2); + EtherCard::packetSend(tcp_header() - gPB + TCP_HEADER_LEN_PLAIN + dlen); } void EtherCard::httpServerReply (uint16_t dlen) { @@ -324,10 +367,6 @@ void EtherCard::httpServerReply (uint16_t dlen) { make_tcp_ack_with_data_noflags(dlen); // send data } -static uint32_t getBigEndianLong(byte offs) { //get the sequence number of packets after an ack from GET - return (((unsigned long)gPB[offs]*256+gPB[offs+1])*256+gPB[offs+2])*256+gPB[offs+3]; -} //thanks to mstuetz for the missing (unsigned long) - static void setSequenceNumber(uint32_t seq) { gPB[TCP_SEQ_H_P] = (seq & 0xff000000 ) >> 24; gPB[TCP_SEQ_H_P+1] = (seq & 0xff0000 ) >> 16; @@ -336,11 +375,11 @@ static void setSequenceNumber(uint32_t seq) { } uint32_t EtherCard::getSequenceNumber() { - return getBigEndianLong(TCP_SEQ_H_P); + return ntohl(*(uint32_t *)(gPB + TCP_SEQ_H_P)); } void EtherCard::httpServerReplyAck () { - make_tcp_ack_from_any(getTcpPayloadLength(),0); // send ack for http request + make_tcp_ack_from_any(getTcpPayloadLength(), 0); // send ack for http request SEQ = getSequenceNumber(); //get the sequence number of packets after an ack from GET } @@ -351,14 +390,27 @@ void EtherCard::httpServerReply_with_flags (uint16_t dlen , uint8_t flags) { SEQ=SEQ+dlen; } +// initialize ethernet frame and IP header +static IpHeader &init_ip_frame( + const uint8_t *destip, + const uint8_t protocol + ) +{ + init_eth_header(client_arp_get(destip), ETHTYPE_IP_V); + init_ip_header(destip); + IpHeader &iph = ip_header(); + iph.version(IP_V4); + iph.ihl(IP_IHL); + iph.dscpEcn = 0; + iph.identification = 0x0; + iph.protocol = protocol; + return iph; +} + void EtherCard::clientIcmpRequest(const uint8_t *destip) { - setMACandIPs(destip); - gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); - gPB[IP_TOTLEN_L_P] = 0x54; - gPB[IP_PROTO_P] = IP_PROTO_ICMP_V; - fill_ip_hdr_checksum(); + IpHeader &iph = init_ip_frame(destip, IP_PROTO_ICMP_V); + iph.totalLen = HTONS(0x54); + fill_ip_hdr_checksum(iph); gPB[ICMP_TYPE_P] = ICMP_TYPE_ECHOREQUEST_V; gPB[ICMP_TYPE_P+1] = 0; // code gPB[ICMP_CHECKSUM_H_P] = 0; @@ -372,14 +424,10 @@ void EtherCard::clientIcmpRequest(const uint8_t *destip) { packetSend(98); } -void EtherCard::ntpRequest (uint8_t *ntpip,uint8_t srcport) { - setMACandIPs(ntpip); - gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); - gPB[IP_TOTLEN_L_P] = 0x4c; - gPB[IP_PROTO_P] = IP_PROTO_UDP_V; - fill_ip_hdr_checksum(); +void EtherCard::ntpRequest (uint8_t *ntpip, uint8_t srcport) { + IpHeader &iph = init_ip_frame(ntpip, IP_PROTO_UDP_V); + iph.totalLen = HTONS(0x4c); + fill_ip_hdr_checksum(iph); gPB[UDP_DST_PORT_H_P] = 0; gPB[UDP_DST_PORT_L_P] = NTP_PORT; // ntp = 123 gPB[UDP_SRC_PORT_H_P] = 10; @@ -390,7 +438,7 @@ void EtherCard::ntpRequest (uint8_t *ntpip,uint8_t srcport) { gPB[UDP_CHECKSUM_L_P] = 0; memset(gPB + UDP_DATA_P, 0, 48); memcpy_P(gPB + UDP_DATA_P,ntpreqhdr,10); - fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + 48,1); + fill_checksum(UDP_CHECKSUM_H_P, iph.spaddr - gPB, 16 + 48, 1); packetSend(90); } @@ -406,16 +454,7 @@ uint8_t EtherCard::ntpProcessAnswer (uint32_t *time,uint8_t dstport_l) { } void EtherCard::udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport) { - setMACandIPs(dip); - // see http://tldp.org/HOWTO/Multicast-HOWTO-2.html - // multicast or broadcast address, https://github.com/njh/EtherCard/issues/59 - if ((dip[0] & 0xF0) == 0xE0 || *((unsigned long*) dip) == 0xFFFFFFFF || !memcmp(broadcastip,dip,IP_LEN)) - EtherCard::copyMac(gPB + ETH_DST_MAC, allOnes); - gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); - gPB[IP_TOTLEN_H_P] = 0; - gPB[IP_PROTO_P] = IP_PROTO_UDP_V; + init_ip_frame(dip, IP_PROTO_UDP_V); gPB[UDP_DST_PORT_H_P] = (dport>>8); gPB[UDP_DST_PORT_L_P] = dport; gPB[UDP_SRC_PORT_H_P] = (sport>>8); @@ -426,13 +465,13 @@ void EtherCard::udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport) } void EtherCard::udpTransmit (uint16_t datalen) { - gPB[IP_TOTLEN_H_P] = (IP_HEADER_LEN+UDP_HEADER_LEN+datalen) >> 8; - gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+UDP_HEADER_LEN+datalen; - fill_ip_hdr_checksum(); + IpHeader &iph = ip_header(); + iph.totalLen = htons(udp_header() - (uint8_t *)&ip_header() + UDP_HEADER_LEN + datalen); + fill_ip_hdr_checksum(iph); gPB[UDP_LEN_H_P] = (UDP_HEADER_LEN+datalen) >>8; gPB[UDP_LEN_L_P] = UDP_HEADER_LEN+datalen; - fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + datalen,1); - packetSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen); + fill_checksum(UDP_CHECKSUM_H_P, (uint8_t *)&iph.spaddr - gPB, 16 + datalen,1); + packetSend(udp_header() - gPB + UDP_HEADER_LEN + datalen); } void EtherCard::sendUdp (const char *data, uint8_t datalen, uint16_t sport, @@ -445,13 +484,9 @@ void EtherCard::sendUdp (const char *data, uint8_t datalen, uint16_t sport, } void EtherCard::sendWol (uint8_t *wolmac) { - setMACandIPs(allOnes, allOnes); - gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - memcpy_P(gPB + IP_P,iphdr,9); - gPB[IP_TOTLEN_L_P] = 0x82; - gPB[IP_PROTO_P] = IP_PROTO_UDP_V; - fill_ip_hdr_checksum(); + IpHeader &iph = init_ip_frame(allOnes, IP_PROTO_UDP_V); + iph.totalLen = HTONS(0x82); + fill_ip_hdr_checksum(iph); gPB[UDP_DST_PORT_H_P] = 0; gPB[UDP_DST_PORT_L_P] = 0x9; // wol = normally 9 gPB[UDP_SRC_PORT_H_P] = 10; @@ -466,22 +501,16 @@ void EtherCard::sendWol (uint8_t *wolmac) { pos += 6; copyMac(gPB + pos, wolmac); } - fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + 102,1); + fill_checksum(UDP_CHECKSUM_H_P, (uint8_t *)&iph.spaddr - gPB, 16 + 102,1); packetSend(pos + 6); } // make a arp request static void client_arp_whohas(const uint8_t *ip_we_search) { - uint8_t *iter = gPB; - EthHeader &eh = *(EthHeader *)(iter); - iter += sizeof(EthHeader); - - ArpHeader &arp = *(ArpHeader *)(iter); - iter += sizeof(ArpHeader); - // set ethernet layer mac addresses - init_eth_header(eh, allOnes, ETHTYPE_ARP_V); + init_eth_header(allOnes, ETHTYPE_ARP_V); + ArpHeader &arp = arp_header(); arp.htype = ETH_ARP_HTYPE_ETHERNET; arp.ptype = ETH_ARP_PTYPE_IPV4; arp.hlen = ETH_LEN; @@ -493,7 +522,7 @@ static void client_arp_whohas(const uint8_t *ip_we_search) { EtherCard::copyIp(arp.tpaddr, ip_we_search); // send ethernet frame - EtherCard::packetSend(iter - gPB); + EtherCard::packetSend((uint8_t *)&arp - gPB + sizeof(ArpHeader)); // mark ip as "waiting" using broadcast mac address EtherCard::arpStoreSet(ip_we_search, allOnes); @@ -537,13 +566,9 @@ void EtherCard::updateBroadcastAddress() } static void client_syn(uint8_t srcport,uint8_t dstport_h,uint8_t dstport_l) { - setMACandIPs(EtherCard::hisip); - gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); - gPB[IP_TOTLEN_L_P] = 44; // good for syn - gPB[IP_PROTO_P] = IP_PROTO_TCP_V; - fill_ip_hdr_checksum(); + IpHeader &iph = init_ip_frame(EtherCard::hisip, IP_PROTO_TCP_V); + iph.totalLen = HTONS(44); // good for syn + fill_ip_hdr_checksum(iph); gPB[TCP_DST_PORT_H_P] = dstport_h; gPB[TCP_DST_PORT_L_P] = dstport_l; gPB[TCP_SRC_PORT_H_P] = TCPCLIENT_SRC_PORT_H; @@ -563,9 +588,9 @@ static void client_syn(uint8_t srcport,uint8_t dstport_h,uint8_t dstport_l) { gPB[TCP_OPTIONS_P+1] = 4; gPB[TCP_OPTIONS_P+2] = (CLIENTMSS>>8); gPB[TCP_OPTIONS_P+3] = (uint8_t) CLIENTMSS; - fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8 +TCP_HEADER_LEN_PLAIN+4,2); + fill_checksum(TCP_CHECKSUM_H_P, (uint8_t *)&iph.spaddr - gPB, 8 + TCP_HEADER_LEN_PLAIN + 4, 2); // 4 is the tcp mss option: - EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4); + EtherCard::packetSend(tcp_header() - gPB + TCP_HEADER_LEN_PLAIN + 4); } uint8_t EtherCard::clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t), @@ -642,7 +667,7 @@ void EtherCard::httpPost (const char *urlbuf, const char *hoststr, const char *a www_fd = clientTcpReq(&www_client_internal_result_cb,&www_client_internal_datafill_cb,hisport); } -static uint16_t tcp_datafill_cb(uint8_t fd) { +static uint16_t tcp_datafill_cb(uint8_t /* fd */) { uint16_t len = Stash::length(); Stash::extract(0, len, EtherCard::tcpOffset()); Stash::cleanup(); @@ -656,11 +681,10 @@ static uint16_t tcp_datafill_cb(uint8_t fd) { return len; } -static uint8_t tcp_result_cb(uint8_t fd, uint8_t status, uint16_t datapos, uint16_t datalen) { +static uint8_t tcp_result_cb(uint8_t fd, uint8_t status, uint16_t datapos, uint16_t /* datalen */) { if (status == 0) { result_fd = fd; // a valid result has been received, remember its session id result_ptr = (char*) ether.buffer + datapos; - // result_ptr[datalen] = 0; } return 1; } @@ -677,15 +701,16 @@ const char* EtherCard::tcpReply (uint8_t fd) { return result_ptr; } -void EtherCard::registerPingCallback (void (*callback)(uint8_t *srcip)) { +void EtherCard::registerPingCallback (const IcmpCallback callback) { icmp_cb = callback; } uint8_t EtherCard::packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost) { - return gPB[IP_PROTO_P]==IP_PROTO_ICMP_V && + const IpHeader &iph = ip_header(); + return iph.protocol == IP_PROTO_ICMP_V && gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREPLY_V && gPB[ICMP_DATA_P]== PINGPATTERN && - check_ip_message_is_from(ip_monitoredhost); + check_ip_message_is_from(iph, ip_monitoredhost); } uint16_t EtherCard::accept(const uint16_t port, uint16_t plen) { @@ -707,7 +732,7 @@ uint16_t EtherCard::accept(const uint16_t port, uint16_t plen) { return pos; } else if (gPB[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) - make_tcp_ack_from_any(0,0); //No data so close connection + make_tcp_ack_from_any(0, 0); //No data so close connection } } return 0; @@ -786,9 +811,11 @@ uint16_t EtherCard::packetLoop (uint16_t plen) { const uint8_t *iter = gPB; const uint8_t *last = gPB + plen; - const EthHeader &eh = *(const EthHeader *)(iter); + const EthHeader &eh = ethernet_header(); iter += sizeof(EthHeader); + // log_data("packetLoop", gPB, plen); + // arp payload if (eh.etype == ETHTYPE_ARP_V) { @@ -796,44 +823,50 @@ uint16_t EtherCard::packetLoop (uint16_t plen) { return 0; } - if (eth_type_is_ip_and_my_ip(plen)==0) + if (eh.etype != ETHTYPE_IP_V) { //Not IP so ignoring - //!@todo Add other protocols (and make each optional at compile time) return 0; } + if ((uint8_t)(last - iter) < sizeof(IpHeader)) + { // not enough data for IP packet + return 0; + } + + const IpHeader &iph = ip_header(); + iter += sizeof(IpHeader); + + if (is_my_ip(iph)==0) + return 0; + // refresh arp store if (memcmp(eh.thaddr, mymac, ETH_LEN) == 0) - { - const IpHeader &ip = *(const IpHeader *)(iter); - iter += sizeof(IpHeader); - arpStoreSet(is_lan(myip, ip.spaddr) ? ip.spaddr : gwip, eh.shaddr); - } + arpStoreSet(is_lan(myip, iph.spaddr) ? iph.spaddr : gwip, eh.shaddr); #if ETHERCARD_ICMP - if (gPB[IP_PROTO_P]==IP_PROTO_ICMP_V && gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V) + if (iph.protocol == IP_PROTO_ICMP_V && gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V) { //Service ICMP echo request (ping) if (icmp_cb) - (*icmp_cb)(&(gPB[IP_SRC_P])); + (*icmp_cb)(iph.spaddr); make_echo_reply_from_request(plen); return 0; } #endif #if ETHERCARD_UDPSERVER - if (ether.udpServerListening() && gPB[IP_PROTO_P]==IP_PROTO_UDP_V) + if (ether.udpServerListening() && iph.protocol ==IP_PROTO_UDP_V) { //Call UDP server handler (callback) if one is defined for this packet - if(ether.udpServerHasProcessedPacket(plen)) + if(ether.udpServerHasProcessedPacket(iph, iter, last)) return 0; //An UDP server handler (callback) has processed this packet } #endif - if (plen<54 || gPB[IP_PROTO_P]!=IP_PROTO_TCP_V ) + if (plen<54 || iph.protocol != IP_PROTO_TCP_V) return 0; //from here on we are only interested in TCP-packets; these are longer than 54 bytes #if ETHERCARD_TCPCLIENT if (gPB[TCP_DST_PORT_H_P]==TCPCLIENT_SRC_PORT_H) { //Source port is in range reserved (by EtherCard) for client TCP/IP connections - if (check_ip_message_is_from(hisip)==0) + if (check_ip_message_is_from(iph, hisip)==0) return 0; //Not current TCP/IP connection (only handle one at a time) if (gPB[TCP_FLAGS_P] & TCP_FLAGS_RST_V) { //TCP reset flagged diff --git a/src/udpserver.cpp b/src/udpserver.cpp index aa3b74f..2b0dc2e 100644 --- a/src/udpserver.cpp +++ b/src/udpserver.cpp @@ -53,7 +53,7 @@ bool EtherCard::udpServerListening() { return numListeners > 0; } -bool EtherCard::udpServerHasProcessedPacket(uint16_t plen) { +bool EtherCard::udpServerHasProcessedPacket(const IpHeader &iph, const uint8_t *iter, const uint8_t *last) { bool packetProcessed = false; for(int i = 0; i < numListeners; i++) { @@ -62,7 +62,7 @@ bool EtherCard::udpServerHasProcessedPacket(uint16_t plen) { uint16_t datalen = (uint16_t) (gPB[UDP_LEN_H_P] << 8) + gPB[UDP_LEN_L_P] - UDP_HEADER_LEN; listeners[i].callback( listeners[i].port, - gPB + IP_SRC_P, + (uint8_t *)iph.spaddr, // TODO: change definition of UdpServerCallback to const uint8_t * (gPB[UDP_SRC_PORT_H_P] << 8) | gPB[UDP_SRC_PORT_L_P], (const char *) (gPB + UDP_DATA_P), datalen);