/*
 * Copyright 2015 Mario Richter, Peter Stephan
 * This file is part of NeMo.
 *
 * NeMo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * NeMo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with NeMo.  If not, see <http://www.gnu.org/licenses/>.
 */

/*!
 * \file net_tcp.c
 * \ingroup net
 * \brief Netzwerk-Routinen zur TCP-Kommunikation.
 */


#include "main.h"

tcpPortListType tcpPortList[TCP_MAX_OPEN_PORTS];
tcpConnListType tcpConnList[TCP_MAX_CONNECTIONS];

uint16_t tcpInitialSeqCnt = 0;


/* %%%%%%%%%%%%%%%%%%% TCP-Funktionen Anwendungsschicht %%%%%%%%%%%%%%%%%%%% */

/*!
 * \ingroup tcp
 * \brief Neuen Port öffnen und Handler definieren.
 *
 * Mit dieser Funktion wird ein neuer TCP-Port geöffnet und diesem Port eine Handler-Funktion
 * zugewiesen. Wird von außen eine neue Verbindung zu diesem Port angefragt, wird diese akzeptiert
 * und die Daten sowie Ereignisse dieser Verbindung werden der Handler-Funktion übergeben.
 *
 * Ist der zu öffnende Port bereits geöffnet, oder sind keine weiteren freien Plätze in der
 * Listen-Liste mehr verfügbar, wird eine Fehlermeldung zurückgegeben.
 *
 * \param port Nummer des zu öffnenden Ports
 * \param handler Zeiger auf die zugehörige Handler-Funktion
 * \return Status: 0=Port geöffnet, 1=Port bereits offen, 2=Kein freier Eintrag verfügbar.
 * \sa tcpUnlisten
 */

uint8_t tcpListen(uint16_t port, connHandler handler)
{
  uint8_t i = 0;
  uint8_t entryFound = 0;
  char buf[6];

  for(i = 0; i < TCP_MAX_OPEN_PORTS; i++)     // Prüfe, ob Port bereits geöffnet
  {
    if(tcpPortList[i].port == port)           // Port bereits geöffnet
    {
      if(debugLevel & DEBUGLVL_STACK_ERROR)
      {
        usart_send_string_P(PSTR("tcpListen: port "));
        snprintf(buf, 6, "%d", port);
        usart_send_string(buf);
        usart_send_line_P(PSTR(" already opened!"));
      }

      return 1;
    }
  }

  for(i = 0; (i < TCP_MAX_OPEN_PORTS) && (entryFound == 0); i++)
  {
    if(tcpPortList[i].port == 0)              // Ist der Eintrag frei?
    {
      tcpPortList[i].port = port;             // Eintrag durchführen
      tcpPortList[i].handler = handler;
      entryFound = 1;
    }
  }

  if(entryFound == 1)                         // Port erfolgreich eingetragen
  {
    if(debugLevel & DEBUGLVL_STACK_INFO)
    {
      usart_send_string_P(PSTR("tcpListen: port "));
      snprintf(buf, 6, "%d", port);
      usart_send_string(buf);
      usart_send_line_P(PSTR(" opened."));
    }

    return 0;
  }
  else                                        // Kein freier Eintrag gefunden
  {
    if(debugLevel & DEBUGLVL_STACK_ERROR)
    {
      usart_send_line_P(PSTR("tcpListen: port list full!"));
    }

    return 2;
  }
}


/*!
 * \ingroup tcp
 * \brief Geöffneten Port schließen
 *
 * Mit dieser Funktion wird ein vorher geöffneter TCP-Port geschlossen, das heißt es werden
 * keine neuen Verbindungen mit dieser Portnummer mehr akzeptiert.
 *
 * Ist der zu schließende Port nicht geöffnet, wird eine Fehlermeldung zurückgegeben.
 *
 * \param port Nummer des zu schließenden Ports.
 * \return Status: 0=Port erfolgreich geschlossen, 1=Port ist nicht geöffnet
 * \sa tcpListen
 */

uint8_t tcpUnlisten(uint16_t port)
{
  uint8_t i = 0;
  uint8_t entryFound = 0;
  char buf[6];

  for(i = 0; (i < TCP_MAX_OPEN_PORTS) && (entryFound == 0); i++)
  {
    if(tcpPortList[i].port == port)           // Eintrag gefunden --> löschen
    {
      tcpPortList[i].port = 0;
      tcpPortList[i].handler = NULL;
      entryFound = 1;
    }
  }

  if(entryFound == 1)                         // Port erfolgreich geschlossen
  {
    if(debugLevel & DEBUGLVL_STACK_INFO)
    {
      usart_send_string_P(PSTR("tcpUnlisten: port "));
      snprintf(buf, 6, "%d", port);
      usart_send_string(buf);
      usart_send_line_P(PSTR(" closed."));
    }

    return 0;
  }
  else                                        // Port in Liste nicht gefunden
  {
    if(debugLevel & DEBUGLVL_STACK_ERROR)
    {
      usart_send_string_P(PSTR("tcpUnlisten: port "));
      snprintf(buf, 6, "%d", port);
      usart_send_string(buf);
      usart_send_line_P(PSTR(" not found!"));
    }

    return 1;
  }
}


/*!
 * \ingroup tcp
 * \brief Verbindung herstellen
 *
 * Diese Funktion stellt aktiv eine TCP-Verbindung zu einer angegebenen Gegenstelle her.
 * Nach dem Verbindungsaufbau wird die angegebene Handlerfunktion aufgerufen, in der z.B.
 * erste Daten gesendet werden können.
 *
 * \param ipAddr Zeiger auf die IP-Adresse der Gegenstelle
 * \param remotePort Port der Gegenstelle
 * \param localPort Eigener Port
 * \param handler Zeiger auf die Handlerfunktion der Verbindung
 * \return Status: 255: Fehler beim Verbindungsaufbau (kein freier Platz in Verbindungsliste);
 * ansonsten: Handle der Verbindung
 *
 * \sa tcpClose
 */

uint8_t tcpOpen(uint8_t * ipAddr, uint16_t remotePort, uint16_t localPort, connHandler handler)
{
  uint8_t * mac;
  uint8_t ind;

  // Prüfe, ob Verbindung bereits existiert
  ind = tcpGetConnHandle(myIpAddr, ipAddr, localPort, remotePort);

  if(ind != 255)
  {
    if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
    {
      usart_send_line_P(PSTR("tcpOpen: conn already exists"));
    }
    return ind;
  }

  // Suche nach freiem Eintrag in TCP-Verbindungsliste
  ind = tcpGetFreeConnEntry();

  // Kein freier Eintrag mehr vorhanden
  if(ind == 255)
  {
    if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
    {
      usart_send_line_P(PSTR("tcpOpen: connList full"));
    }
    return 255;
  }
  else
  {
    // MAC-Adresse zu IP-Adresse ermitteln
    mac = arpGetMac(ipAddr);

    tcpConnList[ind].handler = handler;
    tcpConnList[ind].localPort = localPort;
    tcpConnList[ind].remoteIp[0] = ipAddr[0];
    tcpConnList[ind].remoteIp[1] = ipAddr[1];
    tcpConnList[ind].remoteIp[2] = ipAddr[2];
    tcpConnList[ind].remoteIp[3] = ipAddr[3];
    tcpConnList[ind].remotePort = remotePort;
    tcpConnList[ind].closeAfterSend = 0;

    // MAC-Adresse ist noch nicht bekannt: ARP Request absetzen
    if(mac == NULL)
    {
      tcpConnList[ind].timer = TCP_ARP_REQUEST_TIMEOUT;
      tcpConnList[ind].retryCnt = 0;
      tcpSetConnStatus(ind, TCP_STAT_ARP_REQUEST);
      arpRequest(ipAddr);
    }
    // MAC-Adresse bereits bekannt: Verbindungsanfrage senden
    else
    {
      tcpConnList[ind].iss = tcpInitialSeqCnt;
      tcpConnList[ind].sndUNA = tcpConnList[ind].iss;
      tcpConnList[ind].sndNXT = tcpConnList[ind].iss + 1;
      tcpSetConnStatus(ind, TCP_STAT_SYN_SENT);

      tcpConnList[ind].timer = TCP_ACK_TIMEOUT;
      tcpConnList[ind].retryCnt = 0;

      // Verbindungsanfrage erstellen und senden
      tcpCreatePacket(&pack, mac, ipAddr, remotePort, localPort, tcpConnList[ind].iss, 0, 0, TCP_FLAGS_SYN, TCP_WINDOW_LENGTH);
      enc28SendPacket(pack.buf, HTONS(pack.pkt.ipArp.ip.ipLength) + 14);
    }
  }

  return ind;
}


/*!
 * \ingroup tcp
 * \brief Verbindung trennen
 *
 * Mit Aufruf dieser Funktion wird das Beenden einer TCP-Verbindung initiiert. Hierbei
 * kann die Verbindung bereits von der Gegenseite geschlossen sein, oder das Verbindungsende
 * kann initiativ ausgelöst werden.
 *
 * \param index Index der TCP-Verbindung aus der Verbindungsliste.
 * \return Status: 0:Verbindung wird geschlossen, 1: Ungültiger Index, 2: Verbindung wird
 * bereits geschlossen
 * \sa tcpListen
 */

uint8_t tcpClose(uint8_t index)
{
  uint8_t retval = 0;

  if((index >= TCP_MAX_CONNECTIONS) || (tcpConnList[index].status == TCP_STAT_CLOSED))
  {
    if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
    {
      usart_send_string_P(PSTR("tcpClose: bad index 0x"));
      usart_send_hexnumber(index, 0);
      usart_send_line("");
    }
    retval = 1;
  }
  else
  {
    switch(tcpConnList[index].status)
    {
      case TCP_STAT_ARP_REQUEST:
      case TCP_STAT_SYN_SENT:

        // Wurde nichts in den Sendebuffer eingetragen, Verbindung gleich wieder schließen
        if(tcpConnList[index].txread == tcpConnList[index].txwrit)
        {
          // Eintrag in Verbindungsliste entfernen
          tcpConnList[index].remoteIp[0] = 0;
          tcpConnList[index].remoteIp[1] = 0;
          tcpConnList[index].remoteIp[2] = 0;
          tcpConnList[index].remoteIp[3] = 0;
          tcpConnList[index].remotePort = 0;
          tcpConnList[index].localPort = 0;
          tcpConnList[index].iss = 0;
          tcpConnList[index].irs = 0;
          tcpConnList[index].rcvNXT = 0;
          tcpConnList[index].sndNXT = 0;
          tcpConnList[index].sndUNA = 0;
          tcpConnList[index].timer = 0;
          tcpConnList[index].txread = 0;
          tcpConnList[index].txwrit = 0;
          tcpConnList[index].rxread = 0;
          tcpConnList[index].rxwrit = 0;
          tcpConnList[index].handler = NULL;
          tcpConnList[index].closeAfterSend = 0;
          tcpSetConnStatus(index, TCP_STAT_CLOSED);

          if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
          {
            usart_send_line_P(PSTR("tcpClose: delete TCB"));
          }
        }
        // Stehen bereits Daten im Buffer, dann zunächst warten bis alles gesendet ist
        else
        {
          tcpConnList[index].closeAfterSend = 1;

          if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
          {
            usart_send_line_P(PSTR("tcpClose: closing (SYN_SENT)"));
          }
        }

        retval = 0;
        break;

      case TCP_STAT_SYN_RCVD:
      case TCP_STAT_ESTABLISHED:
      case TCP_STAT_CLOSE_WAIT:

        // Verbindung zum Schließen vormerken (wird dann in tcpTimer erledigt)
        tcpConnList[index].closeAfterSend = 1;

        if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
        {
          usart_send_line_P(PSTR("tcpClose: mark for closing"));
        }

        retval = 0;
        break;

      case TCP_STAT_FIN_WAIT_1:
      case TCP_STAT_FIN_WAIT_2:
      case TCP_STAT_CLOSING:
      case TCP_STAT_LAST_ACK:
      case TCP_STAT_TIME_WAIT:

        // Dieser Fall sollte eigentlich nicht vorkommen ...
        if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
        {
          usart_send_line_P(PSTR("tcpClose: connection closing"));
        }
        retval = 2;
        break;
    }
  }

  return retval;
}


/*!
 * \ingroup tcp
 * \brief TCP-Senderoutine
 *
 * Diese Funktion sendet Daten an die angegebene TCP-Verbindung. Die Verbindung muss geöffnet
 * und zur Übertragung bereit sein, ansonsten kehrt die Funktion mit einer Fehlermeldung zurück.
 *
 * \param ind Index auf die Verbindung in der Verbindungsliste
 * \param data Zeiger auf das erste Byte der Daten
 * \param len Länge der zu sendenden Daten in Bytes
 *
 * \return 0: Daten erfolgreich gesendet, 1: Verbindung nicht bereit,, 2: keine Daten, 3: Indexfehler
 *
 * \sa tcpListen, tcpUnlisten, tcpClose
 */

uint8_t tcpSend(uint8_t ind, uint8_t * data, uint8_t len)
{
  if( (tcpConnList[ind].status == TCP_STAT_CLOSED) ||
      (tcpConnList[ind].status == TCP_STAT_FIN_WAIT_1) ||
      (tcpConnList[ind].status == TCP_STAT_FIN_WAIT_2) ||
      (tcpConnList[ind].status == TCP_STAT_CLOSING) ||
      (tcpConnList[ind].status == TCP_STAT_LAST_ACK) ||
      (tcpConnList[ind].status == TCP_STAT_TIME_WAIT))
  {
    return 1;
  }
  else if(ind > TCP_MAX_CONNECTIONS)
  {
    if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
    {
      usart_send_string_P(PSTR("tcpSend: bad index: 0x"));
      usart_send_hexnumber(ind, 0);
      usart_send_line("");
    }
    return 3;
  }
  else
  {
    // Datenlänge gleich null?
    if(len == 0)
      return 2;
    else
    {
      // Daten in TCP-Sendepuffer übernehmen
      while(len > 0)
      {
        tcpConnList[ind].txbuf[tcpConnList[ind].txwrit] = *data;

        len--;
        data++;

        // Index für Sendepuffer weiterziehen
        tcpConnList[ind].txwrit++;
        if(tcpConnList[ind].txwrit >= TCP_RXTX_BUFLEN)
          tcpConnList[ind].txwrit = 0;
      }
      return 0;
    }
  }
}


/*!
 * \ingroup tcp
 * \brief TCP-Timer-Routine
 *
 * Diese Funktion wird zyklisch aufgerufen und kümmert sich um das Fortführen der TCP-Timer.
 *
 * \sa tcpListen, tcpUnlisten, tcpClose
 */

void tcpTimer(void)
{
  uint8_t i;
  uint16_t j, dataLen;
  uint8_t * mac;
  uint8_t * ptr;

  // Statusausgabe der TCP-Verbindungen
  for(i = 0; i < TCP_MAX_CONNECTIONS; i++)
  {
    switch(tcpConnList[i].status)
    {
      case TCP_STAT_SYN_RCVD:

        // Falls Sendebuffer leer ist und Verbindung geschlossen werden soll, dies gleich tun
        if((tcpConnList[i].txread == tcpConnList[i].txwrit) && (tcpConnList[i].closeAfterSend != 0))
        {
          // FIN senden
          tcpCreatePacket(&pack, arpGetMac((uint8_t *)tcpConnList[i].remoteIp),
                          (uint8_t *)tcpConnList[i].remoteIp,
                          tcpConnList[i].remotePort, tcpConnList[i].localPort,
                          tcpConnList[i].sndNXT, tcpConnList[i].rcvNXT, 0,
                          (TCP_FLAGS_FIN | TCP_FLAGS_ACK), TCP_WINDOW_LENGTH);

          // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
          enc28SendPacket(pack.buf, HTONS(pack.pkt.ipArp.ip.ipLength) + 14);

          // sndNXT weiterziehen (FIN)
          tcpConnList[i].sndNXT++;

          // Weiter in FIN_WAIT_1
          tcpSetConnStatus(i, TCP_STAT_FIN_WAIT_1);
          tcpConnList[i].timer = TCP_ACK_TIMEOUT;
          tcpConnList[i].retryCnt = 0;

          if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
          {
            usart_send_line_P(PSTR("tcpTimer: send FIN"));
          }
        }
        break;

      case TCP_STAT_FIN_WAIT_1:

        if(tcpConnList[i].timer == 0)
        {
          tcpConnList[i].retryCnt++;

          if(tcpConnList[i].retryCnt < TCP_MAX_REPEAT_CNT)
          {
            // FIN erneut senden
            tcpCreatePacket(&pack, arpGetMac((uint8_t *)tcpConnList[i].remoteIp),
                            (uint8_t *)tcpConnList[i].remoteIp,
                            tcpConnList[i].remotePort, tcpConnList[i].localPort,
                            tcpConnList[i].sndNXT-1, tcpConnList[i].rcvNXT, 0,
                            (TCP_FLAGS_FIN | TCP_FLAGS_ACK), TCP_WINDOW_LENGTH);

            // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
            enc28SendPacket(pack.buf, HTONS(pack.pkt.ipArp.ip.ipLength) + 14);

            // Timer neu starten
            tcpConnList[i].timer = TCP_ACK_TIMEOUT;

            if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
            {
              usart_send_line_P(PSTR("tcpTimer: resend FIN"));
            }
          }
          else
          {
            // Verbindung zurücksetzen: RST-Packet erstellen
            tcpCreatePacket(&pack, arpGetMac((uint8_t *)tcpConnList[i].remoteIp),
                            (uint8_t *)tcpConnList[i].remoteIp,
                            tcpConnList[i].remotePort, tcpConnList[i].localPort,
                            tcpConnList[i].sndNXT-1, 0, 0, TCP_FLAGS_RST, 0);

            // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
            enc28SendPacket(pack.buf, HTONS(pack.pkt.ipArp.ip.ipLength) + 14);

            // Zu viele Versuche: Abbruch; Verbindung löschen
            tcpConnList[i].remoteIp[0] = 0;
            tcpConnList[i].remoteIp[1] = 0;
            tcpConnList[i].remoteIp[2] = 0;
            tcpConnList[i].remoteIp[3] = 0;
            tcpConnList[i].remotePort = 0;
            tcpConnList[i].localPort = 0;
            tcpConnList[i].iss = 0;
            tcpConnList[i].irs = 0;
            tcpConnList[i].rcvNXT = 0;
            tcpConnList[i].sndNXT = 0;
            tcpConnList[i].sndUNA = 0;
            tcpConnList[i].timer = 0;
            tcpConnList[i].handler = NULL;
            tcpConnList[i].txread = 0;
            tcpConnList[i].txwrit = 0;
            tcpConnList[i].rxread = 0;
            tcpConnList[i].rxwrit = 0;
            tcpConnList[i].closeAfterSend = 0;
            tcpSetConnStatus(i, TCP_STAT_CLOSED);

            if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
            {
              usart_send_line_P(PSTR("tcpTimer: FIN not answered"));
            }
          }
        }
        else
        {
          tcpConnList[i].timer--;
        }

        break;

      case TCP_STAT_SYN_SENT:

        if(tcpConnList[i].timer == 0)
        {
          tcpConnList[i].retryCnt++;

          if(tcpConnList[i].retryCnt < TCP_MAX_REPEAT_CNT)
          {
            // Verbindungsanfrage erneut senden
            tcpCreatePacket(&pack, arpGetMac((uint8_t *)tcpConnList[i].remoteIp),
                            (uint8_t *)tcpConnList[i].remoteIp,
                            tcpConnList[i].remotePort, tcpConnList[i].localPort,
                            tcpConnList[i].iss, 0, 0,
                            TCP_FLAGS_SYN, TCP_WINDOW_LENGTH);

            // Senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
            enc28SendPacket(pack.buf, HTONS(pack.pkt.ipArp.ip.ipLength) + 14);

            // Timer neu starten
            tcpConnList[i].timer = TCP_ACK_TIMEOUT;

            if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
            {
              usart_send_line_P(PSTR("tcpTimer: resend SYN"));
            }
          }
          else
          {
            // Zu viele Verbindungsversuche: Abbruch
            tcpConnList[i].remoteIp[0] = 0;
            tcpConnList[i].remoteIp[1] = 0;
            tcpConnList[i].remoteIp[2] = 0;
            tcpConnList[i].remoteIp[3] = 0;
            tcpConnList[i].remotePort = 0;
            tcpConnList[i].localPort = 0;
            tcpConnList[i].iss = 0;
            tcpConnList[i].irs = 0;
            tcpConnList[i].rcvNXT = 0;
            tcpConnList[i].sndNXT = 0;
            tcpConnList[i].sndUNA = 0;
            tcpConnList[i].timer = 0;
            tcpConnList[i].handler = NULL;
            tcpConnList[i].txread = 0;
            tcpConnList[i].txwrit = 0;
            tcpConnList[i].rxread = 0;
            tcpConnList[i].rxwrit = 0;
            tcpConnList[i].closeAfterSend = 0;
            tcpSetConnStatus(i, TCP_STAT_CLOSED);

            if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
            {
              usart_send_line_P(PSTR("tcpTimer: SYN not answered"));
            }
          }
        }
        else
        {
          tcpConnList[i].timer--;
        }
        break;

      case TCP_STAT_ESTABLISHED:
      case TCP_STAT_CLOSE_WAIT:

        // Ist der Timer auf 0 (Verbindung im Leerlauf) und stehen Daten im Buffer, diese senden
        if(tcpConnList[i].timer == 0)
        {
          if(tcpConnList[i].txread != tcpConnList[i].txwrit)
          {
            // Daten aus dem Buffer in den Packetspeicher holen.
            // txread wird erst weitergezogen, wenn die Daten bestätigt wurden!
            j = tcpConnList[i].txread;
            ptr = &pack.pkt.ipArp.ip.data.tcp.data;
            dataLen = 0;

            while((j != tcpConnList[i].txwrit) && (dataLen < TCP_RXTX_BUFLEN))
            {
              *ptr++ = tcpConnList[i].txbuf[j++];
              dataLen++;

              if(j >= TCP_RXTX_BUFLEN)
                j = 0;
            }

            // Jetzt die Daten aus dem Zwischenpuffer senden
            tcpCreatePacket(&pack, arpGetMac((uint8_t *)tcpConnList[i].remoteIp),
                            (uint8_t *)tcpConnList[i].remoteIp,
                            tcpConnList[i].remotePort, tcpConnList[i].localPort,
                            tcpConnList[i].sndNXT, tcpConnList[i].rcvNXT, dataLen,
                            (TCP_FLAGS_ACK | TCP_FLAGS_PSH), TCP_WINDOW_LENGTH);

            // Senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
            enc28SendPacket(pack.buf, HTONS(pack.pkt.ipArp.ip.ipLength) + 14);

            // Retry-Counter zurücksetzen (erster Versuch)
            tcpConnList[i].retryCnt = 0;

            if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
            {
              usart_send_string_P(PSTR("tcpTimer ["));
              usart_send_char(i + 0x30);
              usart_send_string_P(PSTR("]: "));
              if(dataLen >= 100)
                usart_send_char((dataLen / 100) + 0x30);
              if(dataLen >= 10)
                usart_send_char(((dataLen % 100) / 10) + 0x30);
              usart_send_char((dataLen % 10) + 0x30);
              usart_send_line_P(PSTR(" bytes sent"));
            }

            // Zeiger für unbestätigte Daten weiterziehen, Sequenznr. des aktuellen Packets merken
            tcpConnList[i].sndNXTlast = tcpConnList[i].sndNXT;
            tcpConnList[i].sndNXT += dataLen;

            // Timer starten für weitere Sendeversuche
            tcpConnList[i].timer = TCP_ACK_TIMEOUT;
          }
          else
          {
            // Ist Verbindung zum Schließen vorgemerkt?
            if((tcpConnList[i].closeAfterSend != 0) || (tcpConnList[i].status == TCP_STAT_CLOSE_WAIT))
            {
              // FIN senden
              tcpCreatePacket(&pack, arpGetMac((uint8_t *)tcpConnList[i].remoteIp),
                              (uint8_t *)tcpConnList[i].remoteIp,
                              tcpConnList[i].remotePort, tcpConnList[i].localPort,
                              tcpConnList[i].sndNXT, tcpConnList[i].rcvNXT, 0,
                              (TCP_FLAGS_FIN | TCP_FLAGS_ACK), TCP_WINDOW_LENGTH);

              // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
              enc28SendPacket(pack.buf, HTONS(pack.pkt.ipArp.ip.ipLength) + 14);

              // sndNXT weiterziehen (FIN)
              tcpConnList[i].sndNXT++;

              if(tcpConnList[i].status == TCP_STAT_CLOSE_WAIT)
              {
                // Aus CLOSE_WAIT weiter in CLOSING
                tcpSetConnStatus(i, TCP_STAT_CLOSING);
              }
              else
              {
                // Sonst weiter in FIN_WAIT_1
                tcpSetConnStatus(i, TCP_STAT_FIN_WAIT_1);
                tcpConnList[i].timer = TCP_ACK_TIMEOUT;
                tcpConnList[i].retryCnt = 0;
              }

              if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
              {
                usart_send_line_P(PSTR("tcpTimer: send FIN"));
              }
            }
          }
        }
        else
        {
          tcpConnList[i].timer--;

          // Wird Timer 0, dann ist Zeit abgelaufen und vorhandene Daten müssen erneut gesendet werden
          if(tcpConnList[i].timer == 0)
          {
            tcpConnList[i].retryCnt++;

            // Max. Anzahl an Versuchen bereits erreicht?
            if(tcpConnList[i].retryCnt >= TCP_MAX_REPEAT_CNT)
            {
              // RST-Packet erstellen
              tcpCreatePacket(&pack, arpGetMac((uint8_t *)tcpConnList[i].remoteIp),
                              (uint8_t *)tcpConnList[i].remoteIp,
                              tcpConnList[i].remotePort, tcpConnList[i].localPort,
                              tcpConnList[i].sndNXTlast, 0, 0, TCP_FLAGS_RST, 0);

              // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
              enc28SendPacket(pack.buf, HTONS(pack.pkt.ipArp.ip.ipLength) + 14);

              // Eintrag in Verbindungsliste entfernen
              tcpConnList[i].remoteIp[0] = 0;
              tcpConnList[i].remoteIp[1] = 0;
              tcpConnList[i].remoteIp[2] = 0;
              tcpConnList[i].remoteIp[3] = 0;
              tcpConnList[i].remotePort = 0;
              tcpConnList[i].localPort = 0;
              tcpConnList[i].iss = 0;
              tcpConnList[i].irs = 0;
              tcpConnList[i].rcvNXT = 0;
              tcpConnList[i].sndNXT = 0;
              tcpConnList[i].sndUNA = 0;
              tcpConnList[i].timer = 0;
              tcpConnList[i].txread = 0;
              tcpConnList[i].txwrit = 0;
              tcpConnList[i].rxread = 0;
              tcpConnList[i].rxwrit = 0;
              tcpConnList[i].handler = NULL;
              tcpSetConnStatus(i, TCP_STAT_CLOSED);

              if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
              {
                usart_send_line_P(PSTR("tcpTimer: conn timed out"));
              }
            }
            else
            {
              if(tcpConnList[i].txread != tcpConnList[i].txwrit)
              {
                // Daten aus dem Buffer in den Packetspeicher holen.
                // txread wird erst weitergezogen, wenn die Daten bestätigt wurden!
                j = tcpConnList[i].txread;
                ptr = &pack.pkt.ipArp.ip.data.tcp.data;
                dataLen = 0;

                while((j != tcpConnList[i].txwrit) && (dataLen < TCP_RXTX_BUFLEN))
                {
                  *ptr++ = tcpConnList[i].txbuf[j++];
                  dataLen++;

                  if(j >= TCP_RXTX_BUFLEN)
                    j = 0;
                }

                // Jetzt die Daten aus dem Zwischenpuffer senden.
                // Wir verwenden die alte Sequenznummer (sndNXTlast), die schon beim ersten Sendeversuch benutzt wurde!
                tcpCreatePacket(&pack, arpGetMac((uint8_t *)tcpConnList[i].remoteIp),
                                (uint8_t *)tcpConnList[i].remoteIp,
                                tcpConnList[i].remotePort, tcpConnList[i].localPort,
                                tcpConnList[i].sndNXTlast, tcpConnList[i].rcvNXT, dataLen,
                                (TCP_FLAGS_ACK | TCP_FLAGS_PSH), TCP_WINDOW_LENGTH);

                // Senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
                enc28SendPacket(pack.buf, HTONS(pack.pkt.ipArp.ip.ipLength) + 14);

                if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
                {
                  usart_send_string_P(PSTR("tcpTimer ["));
                  usart_send_char(i + 0x30);
                  usart_send_string_P(PSTR("]: "));
                  if(dataLen >= 100)
                    usart_send_char((dataLen / 100) + 0x30);
                  if(dataLen >= 10)
                    usart_send_char(((dataLen % 100) / 10) + 0x30);
                  usart_send_char((dataLen % 10) + 0x30);
                  usart_send_line_P(PSTR(" bytes re-sent"));
                }

                // Zeiger für unbestätigte Daten neu berechnen (falls neue Daten hinzugekommen sind)
                tcpConnList[i].sndNXT = tcpConnList[i].sndNXTlast + dataLen;

                // Timer starten für weitere Sendeversuche
                tcpConnList[i].timer = TCP_ACK_TIMEOUT;
              }
            }
          }
        }
        break;

      case TCP_STAT_TIME_WAIT:            // Timeout nach Verbindungsende

        if(tcpConnList[i].timer > 0)
        {
          tcpConnList[i].timer--;
        }
        else
        {
          // Eintrag in Verbindungsliste entfernen
          tcpConnList[i].remoteIp[0] = 0;
          tcpConnList[i].remoteIp[1] = 0;
          tcpConnList[i].remoteIp[2] = 0;
          tcpConnList[i].remoteIp[3] = 0;
          tcpConnList[i].remotePort = 0;
          tcpConnList[i].localPort = 0;
          tcpConnList[i].iss = 0;
          tcpConnList[i].irs = 0;
          tcpConnList[i].rcvNXT = 0;
          tcpConnList[i].sndNXT = 0;
          tcpConnList[i].sndUNA = 0;
          tcpConnList[i].timer = 0;
          tcpConnList[i].handler = NULL;
          tcpConnList[i].txread = 0;
          tcpConnList[i].txwrit = 0;
          tcpConnList[i].rxread = 0;
          tcpConnList[i].rxwrit = 0;
          tcpSetConnStatus(i, TCP_STAT_CLOSED);
        }
        break;

      case TCP_STAT_ARP_REQUEST:            // MAC-Adresse zu neuer Verbindung ermitteln

        if(tcpConnList[i].timer > 0)
        {
          tcpConnList[i].timer--;
        }
        else
        {
          // Prüfe ob MAC jetzt bekannt ist
          mac = arpGetMac((uint8_t *)tcpConnList[i].remoteIp);

          // Nein, MAC ist weiter unbekannt
          if(mac == NULL)
          {
            tcpConnList[i].retryCnt++;

            // Nach drittem Versuch abbrechen
            if(tcpConnList[i].retryCnt >= TCP_MAX_ARP_REPEAT_CNT)
            {
              // Eintrag in Verbindungsliste entfernen
              tcpConnList[i].remoteIp[0] = 0;
              tcpConnList[i].remoteIp[1] = 0;
              tcpConnList[i].remoteIp[2] = 0;
              tcpConnList[i].remoteIp[3] = 0;
              tcpConnList[i].remotePort = 0;
              tcpConnList[i].localPort = 0;
              tcpConnList[i].iss = 0;
              tcpConnList[i].irs = 0;
              tcpConnList[i].rcvNXT = 0;
              tcpConnList[i].sndNXT = 0;
              tcpConnList[i].sndUNA = 0;
              tcpConnList[i].timer = 0;
              tcpConnList[i].handler = NULL;
              tcpConnList[i].txread = 0;
              tcpConnList[i].txwrit = 0;
              tcpConnList[i].rxread = 0;
              tcpConnList[i].rxwrit = 0;
              tcpSetConnStatus(i, TCP_STAT_CLOSED);

              if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
              {
                usart_send_line_P(PSTR("tcpTimer: No MAC replied"));
              }
            }
            // Weiteren Versuch starten
            else
            {
              // Neuen ARP-Request senden, Timer neu starten
              tcpConnList[i].timer = TCP_ARP_REQUEST_TIMEOUT;
              arpRequest((uint8_t *)tcpConnList[i].remoteIp);
            }
          }

          // MAC ist jetzt bekannt
          else
          {
            tcpConnList[i].iss = tcpInitialSeqCnt;
            tcpConnList[i].sndUNA = tcpConnList[i].iss;
            tcpConnList[i].sndNXT = tcpConnList[i].iss + 1;
            tcpSetConnStatus(i, TCP_STAT_SYN_SENT);

            tcpConnList[i].timer = TCP_ACK_TIMEOUT;
            tcpConnList[i].retryCnt = 0;

            // Verbindungsanfrage erstellen und senden
            tcpCreatePacket(&pack, mac, (uint8_t *)tcpConnList[i].remoteIp, tcpConnList[i].remotePort,
                            tcpConnList[i].localPort, tcpConnList[i].iss, 0, 0, TCP_FLAGS_SYN, TCP_WINDOW_LENGTH);
            enc28SendPacket(pack.buf, HTONS(pack.pkt.ipArp.ip.ipLength) + 14);
          }
        }
        break;
    }
  }
}


/*!
 * \ingroup tcp
 * \brief Liste geöffneter Ports anzeigen
 *
 * Diese Funktion gibt eine Liste der geöffneten TCP-Ports aus und zeigt außerdem die
 * Adresse der zugehörigen Handlerfunktion an.
 *
 * \sa tcpListen, tcpUnlisten
 */

void tcpShowPortList(uint8_t ind)
{
  uint8_t i = 0;
  char buf[32];

  sendLine(ind," # Port   Function");
  for(i = 0; i < TCP_MAX_OPEN_PORTS; i++)
  {
    if(tcpPortList[i].port != 0)
    {
      snprintf(buf, 16, " %1u %-6u 0x%x", i, tcpPortList[i].port, (uint16_t)tcpPortList[i].handler << 1);
      sendLine(ind,buf);
    }
  }
}


/*!
 * \ingroup tcp
 * \brief TCP-Verbindungsliste anzeigen
 *
 * Diese Funktion gibt die aktuelle TCP-Verbindungsliste aus.
 *
 * \sa tcpShowPortList
 */

void tcpShowConnList(uint8_t ind)
{
  uint8_t i = 0;
  char buf[32];

  sendLine(ind, " Ch Remote-IP       RPort LPort Timer Status");

  for(i = 0; i < TCP_MAX_CONNECTIONS; i++)
  {
    if(tcpConnList[i].status != TCP_STAT_CLOSED)
    {
      snprintf(buf, 32, " %02u %3u.%3u.%3u.%3u",
               i,
               tcpConnList[i].remoteIp[0],
               tcpConnList[i].remoteIp[1],
               tcpConnList[i].remoteIp[2],
               tcpConnList[i].remoteIp[3]);
      sendString(ind, buf);

      snprintf(buf, 32, " %5u %5u %5u ",
               tcpConnList[i].remotePort,
               tcpConnList[i].localPort,
               tcpConnList[i].timer);
      sendString(ind, buf);

      switch(tcpConnList[i].status)
      {
        case TCP_STAT_ARP_REQUEST:  sendLine(ind, "ARP_REQUEST");     break;
        case TCP_STAT_CLOSE_WAIT:   sendLine(ind, "CLOSE_WAIT");      break;
        case TCP_STAT_CLOSING:      sendLine(ind, "CLOSING");         break;
        case TCP_STAT_ESTABLISHED:  sendLine(ind, "ESTABLISHED");     break;
        case TCP_STAT_FIN_WAIT_1:   sendLine(ind, "FIN_WAIT1");       break;
        case TCP_STAT_FIN_WAIT_2:   sendLine(ind, "FIN_WAIT2");       break;
        case TCP_STAT_LAST_ACK:     sendLine(ind, "LAST_ACK");        break;
        case TCP_STAT_SYN_RCVD:     sendLine(ind, "SYN_RCVD");        break;
        case TCP_STAT_SYN_SENT:     sendLine(ind, "SYN_SENT");        break;
        case TCP_STAT_TIME_WAIT:    sendLine(ind, "TIME_WAIT");       break;
        default:                    sendLine(ind, "XXXXXX");
      }
    }
  }
}



/* %%%%%%%%%%%%%%%%%%% TCP-Funktionen Stack-intern %%%%%%%%%%%%%%%%%%%% */

/*!
 * \ingroup tcp
 * \brief Verarbeitung eines neu eingetroffenen TCP-Packets
 *
 * Diese Funktion verarbeitet ein neu eingetroffenes TCP-Packet.
 *
 * \param pkt Zeiger auf den Packet-Buffer.
 */

void tcpHandlePacket(pktType * packet)
{
  uint8_t i = 0, ind = 0, ackIsAcceptable = 0, justEstablished = 0;
  uint16_t j = 0, dataLen = 0;
  uint8_t * dataPtr;
  char buf[6];

  // Suche den Handle der Verbindung in der Verbindungsliste
  ind = tcpGetConnHandle(packet->pkt.ipArp.ip.ipDestIp,
                         packet->pkt.ipArp.ip.ipSourceIp,
                         HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                         HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort));

  // ****************************************************
  // Verbindung existiert noch nicht

  if(ind >= TCP_MAX_CONNECTIONS)
  {
    // ----------------------------------------------------
    // Port ist geöffnet, Verbindungen möglich

    if(tcpCheckListen(packet) == 1)
    {
      // Eingehendes RST verwerfen und zurück
      if(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_RST)
      {
        return;
      }

      // Eingehendes ACK -> RST-Packet zurücksenden
      else if(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_ACK)
      {
        // RST-Packet erstellen
        tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp, HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort),
            HTONS(packet->pkt.ipArp.ip.data.tcp.destPort), packet->pkt.ipArp.ip.data.tcp.acknowledgeNr, 0, 0, TCP_FLAGS_RST, 0);

        // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
        enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

        if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
        {
          usart_send_string_P(PSTR("tcpHandlePacket: conn reset (on ACK) to "));
          for(i = 0; i < 4; i++)          // IP-Adresse ausgeben
          {
            snprintf(buf, 4, "%u", packet->pkt.ipArp.ip.ipDestIp[i]);
            usart_send_string(buf);
            usart_send_char((i < 3) ? '.' : ' ');
          }
          usart_send_char(':');
          snprintf(buf, 6, "%u", HTONS(packet->pkt.ipArp.ip.data.tcp.destPort));
          usart_send_line(buf);
        }
      }

      // Eingehendes SYN -> Verbindungsanfrage
      else if(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_SYN)
      {
        // Prüfe, ob noch ein freier Platz in der Verbindungsliste vorhanden ist
        i = tcpGetFreeConnEntry();

        // Kein freier Eintrag mehr vorhanden -> Verbindung ablehnen
        if(i >= TCP_MAX_CONNECTIONS)
        {
          // RST-Packet erstellen
          tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
              HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
              packet->pkt.ipArp.ip.data.tcp.acknowledgeNr, 0, 0, TCP_FLAGS_RST, 0);

          // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
          enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

          if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
          {
            usart_send_string_P(PSTR("tcpHandlePacket: conn list full! "));
            for(i = 0; i < 4; i++)          // IP-Adresse ausgeben
            {
              snprintf(buf, 4, "%u", packet->pkt.ipArp.ip.ipDestIp[i]);
              usart_send_string(buf);
              usart_send_char((i < 3) ? '.' : ' ');
            }
            usart_send_char(':');
            snprintf(buf, 6, "%u", HTONS(packet->pkt.ipArp.ip.data.tcp.destPort));
            usart_send_line(buf);
          }
        }

        // Freier Eintrag vorhanden: Verbindung erstellen und bestätigen
        else
        {
          // Eintrag in Verbindungsliste erstellen
          tcpConnList[i].remoteIp[0] = packet->pkt.ipArp.ip.ipSourceIp[0];
          tcpConnList[i].remoteIp[1] = packet->pkt.ipArp.ip.ipSourceIp[1];
          tcpConnList[i].remoteIp[2] = packet->pkt.ipArp.ip.ipSourceIp[2];
          tcpConnList[i].remoteIp[3] = packet->pkt.ipArp.ip.ipSourceIp[3];
          tcpConnList[i].remotePort = HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort);
          tcpConnList[i].localPort = HTONS(packet->pkt.ipArp.ip.data.tcp.destPort);
          tcpConnList[i].irs = HTONL(packet->pkt.ipArp.ip.data.tcp.sequenceNr);
          tcpConnList[i].rcvNXT = tcpConnList[i].irs + 1;
          tcpConnList[i].iss = (uint32_t)tcpInitialSeqCnt;        // Initiale Sequencenr. aus 32 Bit Millisekundencounter
          tcpConnList[i].sndNXT = tcpConnList[i].iss + 1;
          tcpConnList[i].sndUNA = tcpConnList[i].iss;
          tcpConnList[i].timer = 0;
          tcpConnList[i].txread = 0;
          tcpConnList[i].txwrit = 0;
          tcpConnList[i].rxread = 0;
          tcpConnList[i].rxwrit = 0;
          tcpConnList[i].closeAfterSend = 0;
          tcpSetConnStatus(i, TCP_STAT_SYN_RCVD);
          tcpConnList[i].handler = tcpGetPortHandler(tcpConnList[i].localPort);

          // Verbindung bestätigen
          tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
              HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
              tcpConnList[i].iss, tcpConnList[i].rcvNXT, 0, (TCP_FLAGS_SYN | TCP_FLAGS_ACK), TCP_WINDOW_LENGTH);

          // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
          enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

          if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
          {
            usart_send_string_P(PSTR("tcpHandlePacket: new conn to "));
            for(i = 0; i < 4; i++)          // IP-Adresse ausgeben
            {
              snprintf(buf, 4, "%u", packet->pkt.ipArp.ip.ipDestIp[i]);
              usart_send_string(buf);
              usart_send_char((i < 3) ? '.' : ' ');
            }
            usart_send_char(':');
            snprintf(buf, 6, "%u", HTONS(packet->pkt.ipArp.ip.data.tcp.destPort));
            usart_send_line(buf);
          }
        }
      }
    }
  }

  // ****************************************************
  // Verbindung existiert bereits

  else
  {
    if(tcpConnList[ind].status == TCP_STAT_SYN_SENT)
    {
      // Bei ACK Sequenznummer prüfen: Größer als erwartet?
      if(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_ACK)
      {
        if( (HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) <= (tcpConnList[ind].iss)) ||
            (HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) > (tcpConnList[ind].sndNXT)))
        {
          if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
          {
            usart_send_string_P(PSTR("tcpHandlePacket: bad ack -> RST: ACK="));
            usart_send_hexnumber((uint8_t)(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr & 0xFF), 0);
            usart_send_hexnumber((uint8_t)((packet->pkt.ipArp.ip.data.tcp.acknowledgeNr & 0xFF00) >> 8), 0);
            usart_send_hexnumber((uint8_t)((packet->pkt.ipArp.ip.data.tcp.acknowledgeNr & 0xFF0000) >> 16), 0);
            usart_send_hexnumber((uint8_t)((packet->pkt.ipArp.ip.data.tcp.acknowledgeNr & 0xFF000000) >> 24), 0);
            usart_send_string_P(PSTR(" ISS="));
            usart_send_hexnumber((uint8_t)((tcpConnList[ind].iss & 0xFF000000) >> 24), 0);
            usart_send_hexnumber((uint8_t)((tcpConnList[ind].iss & 0xFF0000) >> 16), 0);
            usart_send_hexnumber((uint8_t)((tcpConnList[ind].iss & 0xFF00) >> 8), 0);
            usart_send_hexnumber((uint8_t)(tcpConnList[ind].iss & 0xFF), 0);
            usart_send_string_P(PSTR(" sndNXT="));
            usart_send_hexnumber((uint8_t)((tcpConnList[ind].sndNXT & 0xFF000000) >> 24), 0);
            usart_send_hexnumber((uint8_t)((tcpConnList[ind].sndNXT & 0xFF0000) >> 16), 0);
            usart_send_hexnumber((uint8_t)((tcpConnList[ind].sndNXT & 0xFF00) >> 8), 0);
            usart_send_hexnumber((uint8_t)(tcpConnList[ind].sndNXT & 0xFF), 0);
            usart_send_line("");
          }

          // Ist kein RST-Flag gesetzt, dann Verbindung zurücksetzen
          if(!(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_RST))
          {
            // RST-Packet erstellen
            tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
                            HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                            packet->pkt.ipArp.ip.data.tcp.acknowledgeNr, 0, 0, TCP_FLAGS_RST, 0);

            // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
            enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);
          }

          return;
        }

        // Prüfe ob ACK ist akzeptierbar (wird im Fall eines gesetzten RST-Flags benötigt)
        if( (HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) >= (tcpConnList[ind].sndUNA) &&
            (HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) <= (tcpConnList[ind].sndNXT))))
        {
          ackIsAcceptable = 1;
        }
      }

      // Ist RST-Flag gesetzt?
      if(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_RST)
      {
        if(ackIsAcceptable)
        {
          // Eintrag in Verbindungsliste entfernen
          tcpConnList[ind].remoteIp[0] = 0;
          tcpConnList[ind].remoteIp[1] = 0;
          tcpConnList[ind].remoteIp[2] = 0;
          tcpConnList[ind].remoteIp[3] = 0;
          tcpConnList[ind].remotePort = 0;
          tcpConnList[ind].localPort = 0;
          tcpConnList[ind].iss = 0;
          tcpConnList[ind].irs = 0;
          tcpConnList[ind].rcvNXT = 0;
          tcpConnList[ind].sndNXT = 0;
          tcpConnList[ind].sndUNA = 0;
          tcpConnList[ind].timer = 0;
          tcpConnList[ind].txread = 0;
          tcpConnList[ind].txwrit = 0;
          tcpConnList[ind].rxread = 0;
          tcpConnList[ind].rxwrit = 0;
          tcpConnList[ind].closeAfterSend = 0;
          tcpConnList[ind].handler = NULL;
          tcpSetConnStatus(ind, TCP_STAT_CLOSED);

          if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
          {
            usart_send_string_P(PSTR("tcpHandlePacket: conn refused by "));
            for(i = 0; i < 4; i++)          // IP-Adresse ausgeben
            {
              snprintf(buf, 4, "%u", packet->pkt.ipArp.ip.ipSourceIp[i]);
              usart_send_string(buf);
              usart_send_char((i < 3) ? '.' : ' ');
            }
            usart_send_char(':');
            snprintf(buf, 6, "%u", HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort));
            usart_send_line(buf);
          }
        }

        return;
      }

      // Ist SYN-Flag gesetzt?
      if(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_SYN)
      {
        tcpConnList[ind].irs = HTONL(packet->pkt.ipArp.ip.data.tcp.sequenceNr);
        tcpConnList[ind].rcvNXT = tcpConnList[ind].irs + 1;

        // Falls ACK in Ordnung, dann SND_UNA nachziehen
        if(ackIsAcceptable)
          tcpConnList[ind].sndUNA = HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr);

        // Wenn SND_UNA größer ISS ist, wurde unser eigenes SYN bestätigt!
        if(tcpConnList[ind].sndUNA > tcpConnList[ind].iss)
        {
          // Verbindung ist jetzt hergestellt
          tcpSetConnStatus(ind, TCP_STAT_ESTABLISHED);

          // Timer für Verbindungsaufbau stoppen
          tcpConnList[ind].timer = 0;

          // ACK-Packet erstellen
          tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
                          HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                          tcpConnList[ind].sndNXT, tcpConnList[ind].rcvNXT, 0, TCP_FLAGS_ACK, TCP_WINDOW_LENGTH);

          // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
          enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

          // TODO: Wenn bereits Daten enthalten sind, diese verarbeiten!!

          if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
          {
            usart_send_string_P(PSTR("tcpHandlePacket: conn established: "));
            for(i = 0; i < 4; i++)          // IP-Adresse ausgeben
            {
              snprintf(buf, 4, "%u", packet->pkt.ipArp.ip.ipDestIp[i]);
              usart_send_string(buf);
              usart_send_char((i < 3) ? '.' : ' ');
            }
            usart_send_char(':');
            snprintf(buf, 6, "%u", HTONS(packet->pkt.ipArp.ip.data.tcp.destPort));
            usart_send_line(buf);
          }

          // User-Funktion aufrufen: Verbindung ist hergestellt
          if(tcpConnList[ind].handler != NULL)
            tcpConnList[ind].handler(ind, TCP_HNDL_ESTABLISHED);
        }

        // Eigenes SYN noch nicht bestätigt -> erneutes SYN + ACK senden
        else
        {
          // Status: SYN erhalten
          tcpSetConnStatus(ind, TCP_STAT_SYN_RCVD);

          // SYN+ACK-Packet erstellen
          tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
                          HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                          tcpConnList[ind].iss, tcpConnList[ind].rcvNXT, 0, (TCP_FLAGS_SYN + TCP_FLAGS_ACK), TCP_WINDOW_LENGTH);

          // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
          enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

          // TODO: Wenn bereits Daten enthalten sind, diese zwischenpuffern und nach ESTABLISHED verarbeiten!!

          if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
          {
            usart_send_string_P(PSTR("tcpHandlePacket: SYN+ACK resend: "));
            for(i = 0; i < 4; i++)          // IP-Adresse ausgeben
            {
              snprintf(buf, 4, "%u", packet->pkt.ipArp.ip.ipDestIp[i]);
              usart_send_string(buf);
              usart_send_char((i < 3) ? '.' : ' ');
            }
            usart_send_char(':');
            snprintf(buf, 6, "%u", HTONS(packet->pkt.ipArp.ip.data.tcp.destPort));
            usart_send_line(buf);
          }
        }

      }
    }
    else
    {
      // Prüfe ob Sequenznummer den erwarteten Bereich verlässt
      if( (HTONL(packet->pkt.ipArp.ip.data.tcp.sequenceNr) < tcpConnList[ind].rcvNXT) ||
          (((HTONL(packet->pkt.ipArp.ip.data.tcp.sequenceNr)) + HTONS(packet->pkt.ipArp.ip.ipLength) - 40) >= (tcpConnList[ind].rcvNXT + TCP_WINDOW_LENGTH)))
      {
        if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
        {
          usart_send_string_P(PSTR("tcpHandlePacket "));
          usart_send_hexnumber((uint8_t)((tcpInitialSeqCnt & 0xFF00) >> 8), 0);
          usart_send_hexnumber((uint8_t)(tcpInitialSeqCnt & 0xFF), 0);
          usart_send_string_P(PSTR(": seq out of range: SEQ="));
          usart_send_hexnumber((uint8_t)(packet->pkt.ipArp.ip.data.tcp.sequenceNr & 0xFF), 0);
          usart_send_hexnumber((uint8_t)((packet->pkt.ipArp.ip.data.tcp.sequenceNr & 0xFF00) >> 8), 0);
          usart_send_hexnumber((uint8_t)((packet->pkt.ipArp.ip.data.tcp.sequenceNr & 0xFF0000) >> 16), 0);
          usart_send_hexnumber((uint8_t)((packet->pkt.ipArp.ip.data.tcp.sequenceNr & 0xFF000000) >> 24), 0);
          usart_send_string_P(PSTR(" rcvNXT="));
          usart_send_hexnumber((uint8_t)((tcpConnList[ind].rcvNXT & 0xFF000000) >> 24), 0);
          usart_send_hexnumber((uint8_t)((tcpConnList[ind].rcvNXT & 0xFF0000) >> 16), 0);
          usart_send_hexnumber((uint8_t)((tcpConnList[ind].rcvNXT & 0xFF00) >> 8), 0);
          usart_send_hexnumber((uint8_t)(tcpConnList[ind].rcvNXT & 0xFF), 0);
          usart_send_string_P(PSTR(" ipLength="));
          usart_send_hexnumber((uint8_t)(packet->pkt.ipArp.ip.ipLength & 0xFF), 0);
          usart_send_hexnumber((uint8_t)((packet->pkt.ipArp.ip.ipLength & 0xFF00) >> 8), 0);
          usart_send_line("");
        }

        // Wenn RST nicht gesetzt ist, dann ACK senden
        if(!(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_RST))
        {
          // ACK-Packet erstellen
          tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
                          HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                          tcpConnList[ind].sndNXT, tcpConnList[ind].rcvNXT, 0, TCP_FLAGS_ACK, TCP_WINDOW_LENGTH);

          // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
          enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);
        }
        else  // RST ist gesetzt: Verbindung zurücksetzen
        {
          // Eintrag in Verbindungsliste entfernen
          tcpConnList[ind].remoteIp[0] = 0;
          tcpConnList[ind].remoteIp[1] = 0;
          tcpConnList[ind].remoteIp[2] = 0;
          tcpConnList[ind].remoteIp[3] = 0;
          tcpConnList[ind].remotePort = 0;
          tcpConnList[ind].localPort = 0;
          tcpConnList[ind].iss = 0;
          tcpConnList[ind].irs = 0;
          tcpConnList[ind].rcvNXT = 0;
          tcpConnList[ind].sndNXT = 0;
          tcpConnList[ind].sndUNA = 0;
          tcpConnList[ind].timer = 0;
          tcpConnList[ind].txread = 0;
          tcpConnList[ind].txwrit = 0;
          tcpConnList[ind].rxread = 0;
          tcpConnList[ind].rxwrit = 0;
          tcpConnList[ind].handler = NULL;
          tcpConnList[ind].closeAfterSend = 0;
          tcpSetConnStatus(ind, TCP_STAT_CLOSED);

          if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
          {
            usart_send_line_P(PSTR("tcpHandlePacket: RST set (seq out of range)"));
          }
        }

        // Packet verwerfen
        return;
      }

      // Prüfe ob RST gesetzt ist (Verbindung wurde zurückgesetzt)
      if(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_RST)
      {
        // Eintrag in Verbindungsliste entfernen
        tcpConnList[ind].remoteIp[0] = 0;
        tcpConnList[ind].remoteIp[1] = 0;
        tcpConnList[ind].remoteIp[2] = 0;
        tcpConnList[ind].remoteIp[3] = 0;
        tcpConnList[ind].remotePort = 0;
        tcpConnList[ind].localPort = 0;
        tcpConnList[ind].iss = 0;
        tcpConnList[ind].irs = 0;
        tcpConnList[ind].rcvNXT = 0;
        tcpConnList[ind].sndNXT = 0;
        tcpConnList[ind].sndUNA = 0;
        tcpConnList[ind].timer = 0;
        tcpConnList[ind].txread = 0;
        tcpConnList[ind].txwrit = 0;
        tcpConnList[ind].rxread = 0;
        tcpConnList[ind].rxwrit = 0;
        tcpConnList[ind].handler = NULL;
        tcpConnList[ind].closeAfterSend = 0;
        tcpSetConnStatus(ind, TCP_STAT_CLOSED);

        if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
        {
          usart_send_line_P(PSTR("tcpHandlePacket: RST set"));
        }
        return;
      }

      // Ist SYN gesetzt, ist das ein Fehler -> Reset und TCB löschen
      if(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_SYN)
      {
        // Eintrag in Verbindungsliste entfernen
        tcpConnList[ind].remoteIp[0] = 0;
        tcpConnList[ind].remoteIp[1] = 0;
        tcpConnList[ind].remoteIp[2] = 0;
        tcpConnList[ind].remoteIp[3] = 0;
        tcpConnList[ind].remotePort = 0;
        tcpConnList[ind].localPort = 0;
        tcpConnList[ind].iss = 0;
        tcpConnList[ind].irs = 0;
        tcpConnList[ind].rcvNXT = 0;
        tcpConnList[ind].sndNXT = 0;
        tcpConnList[ind].sndUNA = 0;
        tcpConnList[ind].timer = 0;
        tcpConnList[ind].txread = 0;
        tcpConnList[ind].txwrit = 0;
        tcpConnList[ind].rxread = 0;
        tcpConnList[ind].rxwrit = 0;
        tcpConnList[ind].handler = NULL;
        tcpConnList[ind].closeAfterSend = 0;
        tcpSetConnStatus(ind, TCP_STAT_CLOSED);

        // RST-Packet erstellen
        tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
                        HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                        tcpConnList[ind].sndNXT, 0, 0, TCP_FLAGS_RST, 0);

        // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
        enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

        if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
        {
          usart_send_line_P(PSTR("tcpHandlePacket: SYN set"));
        }
        return;
      }

      // Weiter nur, wenn ACK gesetzt ist
      if(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_ACK)
      {
        switch(tcpConnList[ind].status)
        {
          case TCP_STAT_SYN_RCVD:

            // Ist ACK-Nummer im erwarteten Bereich, ist Verbindung hergestellt
            if( (HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) >= tcpConnList[ind].sndUNA) &&
                (HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) <= tcpConnList[ind].sndNXT))
            {
              tcpSetConnStatus(ind, TCP_STAT_ESTABLISHED);

              // Merker, dass Verbindung gerade hergestellt wurde
              justEstablished = 1;

              // User-Funktion aufrufen: Verbindung ist hergestellt
              if(tcpConnList[ind].handler != NULL)
                tcpConnList[ind].handler(ind, TCP_HNDL_ESTABLISHED);
            }
            // andernfalls RST-Packet senden
            else
            {
              // RST-Packet erstellen
              tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
                              HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                              HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr), 0, 0, TCP_FLAGS_RST, 0);

              // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
              enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

              if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
              {
                usart_send_line_P(PSTR("tcpHandlePacket: bad ack -> reset"));
              }
              return;
            }
            /* no break */

          case TCP_STAT_ESTABLISHED:
          case TCP_STAT_FIN_WAIT_1:
          case TCP_STAT_FIN_WAIT_2:
          case TCP_STAT_CLOSE_WAIT:
          case TCP_STAT_CLOSING:
          case TCP_STAT_LAST_ACK:
          case TCP_STAT_TIME_WAIT:

            // Bestätigung bereits erhalten
            if(HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) < tcpConnList[ind].sndUNA)
            {
              // ACK kann ignoriert werden
              if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
              {
                usart_send_line_P(PSTR("tcpHandlePacket: already ACK"));
              }
              return;
            }

            // Bestätigung für neue Daten erhalten
            if( (HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) >  tcpConnList[ind].sndUNA) &&
                (HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) <= tcpConnList[ind].sndNXT))
            {
              // Anzahl neu bestätigter Bytes ermitteln und Sendebuffer weiterziehen
              j = (uint16_t)(HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) - tcpConnList[ind].sndUNA);

              // In den Status ESTABLISHED und CLOSE_WAIT prüfen, ob Daten bestätigt wurden
              // Wurde Verbindung gerade hergestellt, dann gilt das ACK unserem SYN-Packet -> nicht auswerten!
              if(((tcpConnList[ind].status == TCP_STAT_ESTABLISHED) ||
                  (tcpConnList[ind].status == TCP_STAT_CLOSE_WAIT)) &&
                  (justEstablished == 0))
              {
                while((j > 0) && (tcpConnList[ind].txread != tcpConnList[ind].txwrit))
                {
                  tcpConnList[ind].txread++;
                  if(tcpConnList[ind].txread >= TCP_RXTX_BUFLEN)
                    tcpConnList[ind].txread = 0;

                  // sndNXTlast weiterziehen, alte Bytes dürfen wir jetzt nicht nochmal senden!
                  tcpConnList[ind].sndNXTlast++;

                  j--;
                }

                // Wenn keine Daten mehr im Sendebuffer stehen, Timer anhalten
                if(tcpConnList[ind].txread == tcpConnList[ind].txwrit)
                  tcpConnList[ind].timer = 0;
              }

              // sndUNA weiterziehen auf die erhaltene ACK-Nummer
              tcpConnList[ind].sndUNA = HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr);
            }

            // Bestätigung für noch nicht gesendete Daten
            if(HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) > tcpConnList[ind].sndNXT)
            {
              // ACK senden
              tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
                              HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                              tcpConnList[ind].sndNXT, tcpConnList[ind].rcvNXT, 0, TCP_FLAGS_ACK, TCP_WINDOW_LENGTH);

              // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
              enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

              if(debugLevel & DEBUGLVL_STACK_TCP_ERROR)
              {
                usart_send_line_P(PSTR("tcpHandlePacket: not sent ACK"));
              }
              return;
            }

            // Ist alles bestätigt und wir sind in FIN_WAIT_1, wurde unser eigenes FIN bestätigt
            if( (tcpConnList[ind].status == TCP_STAT_FIN_WAIT_1) &&
                ((HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) == tcpConnList[ind].sndNXT)))
            {
              tcpSetConnStatus(ind, TCP_STAT_FIN_WAIT_2);
            }

            // Falls wir in CLOSING sind und unser FIN bestätigt ist, weiter zu TIME_WAIT
            if( (tcpConnList[ind].status == TCP_STAT_CLOSING) &&
                ((HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) == tcpConnList[ind].sndNXT)))
            {
              tcpSetConnStatus(ind, TCP_STAT_TIME_WAIT);
              tcpConnList[ind].timer = TCP_TIMEWAIT_TIME;
            }

            // Sind wir in LAST_ACK und wurde unser letztes FIN bestätigt, dann TCB schließen
            if( (tcpConnList[ind].status == TCP_STAT_LAST_ACK) &&
                ((HTONL(packet->pkt.ipArp.ip.data.tcp.acknowledgeNr) == tcpConnList[ind].sndNXT)))
            {
              // Eintrag in Verbindungsliste entfernen
              tcpConnList[ind].remoteIp[0] = 0;
              tcpConnList[ind].remoteIp[1] = 0;
              tcpConnList[ind].remoteIp[2] = 0;
              tcpConnList[ind].remoteIp[3] = 0;
              tcpConnList[ind].remotePort = 0;
              tcpConnList[ind].localPort = 0;
              tcpConnList[ind].iss = 0;
              tcpConnList[ind].irs = 0;
              tcpConnList[ind].rcvNXT = 0;
              tcpConnList[ind].sndNXT = 0;
              tcpConnList[ind].sndUNA = 0;
              tcpConnList[ind].timer = 0;
              tcpConnList[ind].txread = 0;
              tcpConnList[ind].txwrit = 0;
              tcpConnList[ind].rxread = 0;
              tcpConnList[ind].rxwrit = 0;
              tcpConnList[ind].handler = NULL;
              tcpConnList[ind].closeAfterSend = 0;
              tcpSetConnStatus(ind, TCP_STAT_CLOSED);
            }

            // In TIME_WAIT kann nur erneutes FIN empfangen werden -> bestätigen und Timeout neu starten
            if(tcpConnList[ind].status == TCP_STAT_TIME_WAIT)
            {
              // ACK zusammenbauen und senden
              tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
                              HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                              tcpConnList[ind].sndNXT, tcpConnList[ind].rcvNXT, 0, TCP_FLAGS_ACK, TCP_WINDOW_LENGTH);

              // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
              enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

              // Timeout neu starten
              tcpConnList[ind].timer = TCP_TIMEWAIT_TIME;
            }
        }
      }

      // Prüfe ob das Segment Daten enthält: Berechne Anzahl der Datenbytes
      // TCP-Dataoffset enthält in den oberen 4 Bits die Anzahl an 32 Bit-Wörtern im Header
      // ==> (Dataoffset >> 4) * 4 ==> wird vereinfacht zu: Dataoffset >> 2
      dataLen = HTONS(packet->pkt.ipArp.ip.ipLength) - 20 - (packet->pkt.ipArp.ip.data.tcp.dataOffset >> 2);

      // Ist datalen > 0, enthält das Packet Nutzdaten
      if(dataLen > 0)
      {
        // Acknowledgezähler weiterziehen
        tcpConnList[ind].rcvNXT += dataLen;

        // Empfangene Daten in Buffer übernehmen
        dataPtr = &packet->pkt.ipArp.ip.data.tcp.data;

        while(dataLen > 0)
        {
          tcpConnList[ind].rxbuf[tcpConnList[ind].rxwrit++] = *dataPtr++;

          if(tcpConnList[ind].rxwrit >= TCP_RXTX_BUFLEN)
            tcpConnList[ind].rxwrit = 0;

          dataLen--;
        }

        // Daten bestätigen
        tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
                        HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                        tcpConnList[ind].sndNXT, tcpConnList[ind].rcvNXT, 0, TCP_FLAGS_ACK, TCP_WINDOW_LENGTH);

        // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
        enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

        // Userfunktion aufrufen und neu eingetroffene Daten signalisieren
        if(tcpConnList[ind].handler != NULL)
          tcpConnList[ind].handler(ind, TCP_HNDL_NEWDATA);
      }

      // Prüfe, ob FIN gesetzt ist (Verbindung soll geschlossen werden)
      if(packet->pkt.ipArp.ip.data.tcp.flags & TCP_FLAGS_FIN)
      {
        if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
        {
          usart_send_line_P(PSTR("tcpHandlePacket: conn closing"));
        }

        // Das folgende Packet soll das eben eingetroffene FIN-Packet bestätigen
        // Es werden keine weiteren Daten vom Gegenüber erwartet
        tcpConnList[ind].rcvNXT = HTONL(packet->pkt.ipArp.ip.data.tcp.sequenceNr) + 1;

        // ACK zusammenbauen und senden
        tcpCreatePacket(packet, packet->pkt.sourceMac, packet->pkt.ipArp.ip.ipSourceIp,
                        HTONS(packet->pkt.ipArp.ip.data.tcp.sourcePort), HTONS(packet->pkt.ipArp.ip.data.tcp.destPort),
                        tcpConnList[ind].sndNXT, tcpConnList[ind].rcvNXT, 0, TCP_FLAGS_ACK, TCP_WINDOW_LENGTH);

        // Antwort senden: IP-Paketlänge + 14 Bytes Ethernetheader (OHNE Ethernet-Checksumme!)
        enc28SendPacket(packet->buf, HTONS(packet->pkt.ipArp.ip.ipLength) + 14);

        switch(tcpConnList[ind].status)
        {
          case TCP_STAT_SYN_RCVD:
          case TCP_STAT_ESTABLISHED:

            tcpSetConnStatus(ind, TCP_STAT_CLOSE_WAIT);

            // Userfunktion aufrufen und neu eingetroffene Daten signalisieren
            if(tcpConnList[ind].handler != NULL)
              tcpConnList[ind].handler(ind, TCP_HNDL_CLOSING);
            break;

          case TCP_STAT_FIN_WAIT_1:

            // Prüfe ob unser eigenes FIN mit diesem Packet bestätigt wird
            if(tcpConnList[ind].sndNXT == tcpConnList[ind].sndUNA)
            {
              tcpSetConnStatus(ind, TCP_STAT_TIME_WAIT);
              tcpConnList[ind].timer = TCP_TIMEWAIT_TIME;
            }
            else
            {
              tcpSetConnStatus(ind, TCP_STAT_CLOSING);
            }
            break;

          case TCP_STAT_FIN_WAIT_2:

            // Timer für Verbindungsende starten
            tcpSetConnStatus(ind, TCP_STAT_TIME_WAIT);
            tcpConnList[ind].timer = TCP_TIMEWAIT_TIME;
            break;

          case TCP_STAT_TIME_WAIT:

            // Timer für Verbindungsende neu starten
            tcpConnList[ind].timer = TCP_TIMEWAIT_TIME;
            break;

          default:
            break;
        }
      }
    }
  }
}


/*!
}
 * \ingroup tcp
 * \brief TCP-Packet erstellen
 *
 * Diese Funktion erstellt ein TCP-Packet im Buffer und trägt MAC- und IP-Adressen,
 * Portnummern sowie Sequenz- und ACK-Nummern ein. Anschließend werden durch Aufruf von
 * \ref tcpCreateHeader die IP- und TCP-Header erstellt. Wichtig: Sind zu sendende Daten
 * vorhanden, müssen diese VORAB in den Packetbuffer kopiert werden. Die Länge der
 * Daten wird dann über den Parameter len übergeben.
 *
 * \param packet Zeiger auf den Packet-Buffer, der TCP-Packet enthält.
 * \param destMac Zeiger auf Array mit der Ziel-MAC-Adresse
 * \param destIp Zeiger auf Array mit der Ziel-IP-Adresse
 * \param destPort Ziel-Portnummer
 * \param srcPort Quell-Portnummer
 * \param seqNr Sequenznummer des Packets
 * \param ackNr Acknowledgenummer des Packets
 * \param len Anzahl der zu übertragenden Bytes
 * \param flags Flags für das TCP-Packet (SYN, ACK, FIN, RST, URG)
 * \param window Größe des Empfangsfensters in Byte
 *
 * \sa tcpCreateHeader
 */

void tcpCreatePacket(pktType * packet, uint8_t * destMac, uint8_t * destIp, uint16_t destPort, uint16_t srcPort,
                     uint32_t seqNr, uint32_t ackNr, uint16_t len, uint16_t flags, uint16_t window)
{
  uint8_t i;

  // Packet vorbereiten
  for(i = 0; i < 6; i++)
  {
    // Ethernet Header: MAC-Adressen setzen
    packet->pkt.destinationMac[i] = destMac[i];
    packet->pkt.sourceMac[i] = myMacAddr[i];
  }

  for(i = 0; i < 4; i++)
  {
    // IP-Header: IP-Adressen setzen
    packet->pkt.ipArp.ip.ipDestIp[i] = destIp[i];
    packet->pkt.ipArp.ip.ipSourceIp[i] = myIpAddr[i];
  }

  // Packet Type: IPv4
  packet->pkt.protType = HTONS(ETH_PACKET_TYPE_IPV4);

  // Portnummern
  packet->pkt.ipArp.ip.data.tcp.sourcePort = HTONS(srcPort);
  packet->pkt.ipArp.ip.data.tcp.destPort = HTONS(destPort);

  // Sequenz- und ACK-Nummer
  packet->pkt.ipArp.ip.data.tcp.sequenceNr = HTONL(seqNr);
  packet->pkt.ipArp.ip.data.tcp.acknowledgeNr = HTONL(ackNr);

  // Header erstellen
  packet->pkt.ipArp.ip.ipVerIhl = 0x45;
  packet->pkt.ipArp.ip.ipTos = 0x00;
  packet->pkt.ipArp.ip.ipLength = HTONS(len + 40);
  packet->pkt.ipArp.ip.ipId = HTONS(0);
  packet->pkt.ipArp.ip.ipFlagsOffset = HTONS(0x4000);
  packet->pkt.ipArp.ip.ipTtl = IP_MAX_TTL;
  packet->pkt.ipArp.ip.ipProtType = IP_PACKET_TYPE_TCP;
  packet->pkt.ipArp.ip.ipChecksum = 0;            // wird gleich berechnet, zuerst auf 0
  packet->pkt.ipArp.ip.ipChecksum = HTONS(calcChecksum(&packet->pkt.ipArp.ip.ipVerIhl, 20));

  packet->pkt.ipArp.ip.data.tcp.dataOffset = 5 << 4;
  packet->pkt.ipArp.ip.data.tcp.flags = flags;
  packet->pkt.ipArp.ip.data.tcp.receiveWindow = HTONS(window);
  packet->pkt.ipArp.ip.data.tcp.urgentPtr = HTONS(0);
  packet->pkt.ipArp.ip.data.tcp.checksum = 0;     // wird gleich berechnet, zuerst auf 0
  packet->pkt.ipArp.ip.data.tcp.checksum = HTONS(tcpCalcChecksum(packet));
}


/*!
 * \ingroup tcp
 * \brief Handle einer Verbindung eines eingetroffenen Pakets ermitteln
 *
 * Diese Funktion überprüft für ein eingegangenes TCP-Paket, ob für die zugehörige Verbindung bereits
 * ein Eintrag in der Verbindungsliste existiert und gibt diesen als Wert zurück. Ist noch kein Eintrag
 * vorhanden (d.h. die Verbindung existiert nicht), wird 255 zurückgeliefert. Wird für einen Port 0 angegeben,
 * wird dieser Port nicht überprüft.
 *
 * \param packet Zeiger auf den Packet-Buffer, der das eingehende TCP-Packet enthält.
 * \return Handle der zugehörigen TCP-Verbindung, oder 255 falls nicht existent.
 */

uint8_t tcpGetConnHandle(uint8_t * localIp, uint8_t * remoteIp, uint16_t localPort, uint16_t remotePort)
{
  uint8_t i, ind;

  ind = 255;

  // Verbindungsliste durchgehen und bei erstem passenden Eintrag stoppen
  for(i = 0; (i < TCP_MAX_CONNECTIONS) && (ind == 255); i++)
  {
    // Prüfen, ob aktueller Eintrag auf eingegangenes Packet passt
    if( (myIpAddr[0]                == localIp[0]) &&
        (myIpAddr[1]                == localIp[1]) &&
        (myIpAddr[2]                == localIp[2]) &&
        (myIpAddr[3]                == localIp[3]) &&
        (tcpConnList[i].remoteIp[0] == remoteIp[0]) &&
        (tcpConnList[i].remoteIp[1] == remoteIp[1]) &&
        (tcpConnList[i].remoteIp[2] == remoteIp[2]) &&
        (tcpConnList[i].remoteIp[3] == remoteIp[3]) &&
        ((tcpConnList[i].remotePort == remotePort) || (remotePort == 0)) &&
        ((tcpConnList[i].localPort  == localPort)  || (localPort == 0)))
    {
      // Eintrag passt -> Index übernehmen
      ind = i;
    }
  }

  return ind;
}


/*!
 * \ingroup tcp
 * \brief Handlerfunktion eines gegebenen Ports ermitteln
 *
 * Diese Funktion ermittelt zu einem gegebenen Port die zugehörige Handlerfunktion und gibt
 * diese zurück. Kann dem Port keine Funktion zugeordnet werden, wird NULL zurückgeliefert.
 *
 * \param port Portnummer, zu dem die Handlerfunktion ermittelt werden soll.
 * \return Zugehörige Handlerfunktion, oder NULL falls nicht verfügbar.
 */

connHandler tcpGetPortHandler(uint16_t port)
{
  uint8_t i;
  connHandler handler = NULL;

  // Alle Ports durchgehen und entsprechende Handlerfunktion ausgeben
  for(i = 0; (i < TCP_MAX_OPEN_PORTS) && (handler == NULL); i++)
  {
    if(tcpPortList[i].port == port)
      handler = tcpPortList[i].handler;
  }

  return handler;
}


/*!
 * \ingroup tcp
 * \brief Prüfe ob TCP-Port für eingehende Packets geöffnet ist (Listen)
 *
 * Diese Funktion prüft, ob der angesprochene Port eines eingehenden Packets geöffnet ist,
 * ob also Verbindungen auf diesem Port akzeptiert werden. Es wird 1 zurückgeliefert, falls der
 * Port geöffnet ist, 0 falls nicht.
 *
 * \param packet Zeiger auf den Packet-Buffer, der das eingehende TCP-Packet enthält.
 * \return 1 falls Port geöffnet ist, 0 falls nicht.
 */

uint8_t tcpCheckListen(pktType * packet)
{
  uint8_t entryFound, i;

  entryFound = 0;

  // Portliste durchgehen und bei erstem passenden Eintrag stoppen
  for(i = 0; (i < TCP_MAX_OPEN_PORTS) && (entryFound == 0); i++)
  {
    if( (tcpPortList[i].port == HTONS(packet->pkt.ipArp.ip.data.tcp.destPort)))
      entryFound = 1;
  }

  return entryFound;
}


/*!
 * \ingroup tcp
 * \brief Prüfe auf freie Einträge in der TCP-Verbindungsliste
 *
 * Diese Funktion prüft, ob freie Einträge in der TCP-Verbindungsliste existieren, d.h. ob neue
 * Verbindungen akzeptiert werden können. Sie liefert den Index des ersten freien Eintrags zurück,
 * oder 255, falls alle Einträge belegt sind.
 *
 * \return Index des ersten freien Eintrags, bzw. 255 falls alle belegt.
 */

uint8_t tcpGetFreeConnEntry(void)
{
  uint8_t freeEntry, i;

  freeEntry = 255;

  // Liste durchgehen, alle Einträge mit Status CLOSED abzählen
  for(i = 0; (i < TCP_MAX_CONNECTIONS) && (freeEntry == 255); i++)
  {
    if(tcpConnList[i].status == TCP_STAT_CLOSED)
      freeEntry = i;
  }

  return freeEntry;
}


/*!
 * \ingroup tcp
 * \brief Neuen Status für TCP-Verbindung setzen
 *
 * Diese Funktion setzt einen neuen Status für eine TCP-Verbindung und gibt gegebenenfalls
 * eine Meldung über den neuen Status auf der seriellen Schnittstelle aus, falls der Debugstatus
 * entsprechend gesetzt ist.
 *
 * \param index Index der TCP-Verbindung, für die neuer Status gesetzt werden soll
 * \param status Neuer zu setzender Status
 * \return 1, falls Status neu, oder 0, falls Status unverändert bleibt
 */

uint8_t tcpSetConnStatus(uint8_t index, uint8_t status)
{
  uint8_t retval = 0;

  if(tcpConnList[index].status != status)
  {
    tcpConnList[index].status = status;
    retval = 1;

    if(debugLevel & DEBUGLVL_STACK_TCP_INFO)
    {
      usart_send_string_P(PSTR("TCP ("));
      usart_send_char(index + 0x30);
      usart_send_string_P(PSTR(") status "));

      switch(status)
      {
        case TCP_STAT_CLOSED:       usart_send_line_P(PSTR("CLOSED"));        break;
        case TCP_STAT_CLOSE_WAIT:   usart_send_line_P(PSTR("CLOSE_WAIT"));    break;
        case TCP_STAT_CLOSING:      usart_send_line_P(PSTR("CLOSING"));       break;
        case TCP_STAT_ESTABLISHED:  usart_send_line_P(PSTR("ESTABLISHED"));   break;
        case TCP_STAT_FIN_WAIT_1:   usart_send_line_P(PSTR("FIN_WAIT_1"));    break;
        case TCP_STAT_FIN_WAIT_2:   usart_send_line_P(PSTR("FIN_WAIT_2"));    break;
        case TCP_STAT_LAST_ACK:     usart_send_line_P(PSTR("LAST_ACK"));      break;
        case TCP_STAT_SYN_RCVD:     usart_send_line_P(PSTR("SYN_RCVD"));      break;
        case TCP_STAT_SYN_SENT:     usart_send_line_P(PSTR("SYN_SENT"));      break;
        case TCP_STAT_TIME_WAIT:    usart_send_line_P(PSTR("TIME_WAIT"));     break;
        case TCP_STAT_ARP_REQUEST:  usart_send_line_P(PSTR("ARP_REQUEST "));  break;
        default:                    usart_send_line_P(PSTR("UNKNOWN"));
      }
    }

    usart_wait_send();
  }

  return retval;
}


/*!
 * \ingroup net
 * \brief TCP-Checksummenberechnung (16 Bit)
 *
 * Diese Funktion berechnet die 16-Bit-Checksumme für ein TCP-Packet und gibt diese zurück. Dabei wird
 * zusätzlich zu den Daten des TCP-Packets auch der Pseudoheader mit in die Berechnung einbezogen.
 * Die Daten werden zunächst in 16-Bit-Pakete gestapelt und aufsummiert (bei ungerader Anzahl
 * von Bytes wandert das letzte Byte in das höhere Byte des Wortes). Anschließend wird aus dieser
 * Summe das Einerkomplement gebildet, das die Prüfsumme ergibt.
 *
 * \param packet Zeiger auf den Packet-Buffer mit dem vorbereiteten TCP-Packet.
 * \return Checksumme (16 Bit)
 */

uint16_t tcpCalcChecksum(pktType * packet)
{
  uint16_t len;
  uint32_t res32 = 0;
  uint16_t res16 = 0;
  uint8_t * buf;

  // Zeiger auf erstes Byte des TCP-Headers (folgt auf letztes Byte der Ziel-IP-Adresse)
  buf = &(packet->pkt.ipArp.ip.ipDestIp[3]) + 1;

  // Länge des TCP-Packets aus IP-Länge berechnen (IP-Header: 20 Byte)
  len = HTONS(packet->pkt.ipArp.ip.ipLength) - 20;

  // Mit Pseudo-Header beginnen: Quell-IP, Ziel-IP, TCP-Kennung und Länge
  res16  = ((uint16_t)packet->pkt.ipArp.ip.ipSourceIp[0] << 8) + (uint16_t)(packet->pkt.ipArp.ip.ipSourceIp[1]);
  res32  = (uint32_t)res16;
  res16  = ((uint16_t)packet->pkt.ipArp.ip.ipSourceIp[2] << 8) + (uint16_t)(packet->pkt.ipArp.ip.ipSourceIp[3]);
  res32 += (uint32_t)res16;
  res16  = ((uint16_t)packet->pkt.ipArp.ip.ipDestIp[0] << 8) + (uint16_t)(packet->pkt.ipArp.ip.ipDestIp[1]);
  res32 += (uint32_t)res16;
  res16  = ((uint16_t)packet->pkt.ipArp.ip.ipDestIp[2] << 8) + (uint16_t)(packet->pkt.ipArp.ip.ipDestIp[3]);
  res32 += (uint32_t)res16;
  res32 += (uint32_t)0x06;
  res32 += (uint32_t)len;

  // Jetzt das eigentliche TCP-Packet wortweise hinzuaddieren
  while(len > 1)
  {
    res16  = (uint16_t)(*buf++) << 8;         // 16 Bit-Wort aus zwei Bytes bilden
    res16 += (uint16_t)(*buf++);
    res32 += res16;                           // Wort zu Summe hinzuaddieren
    len -= 2;                                 // Zwei Bytes wurden verarbeitet
  }

  if(len > 0)                                 // Bei ungerader Länge: Letztes Byte behandeln
  {
    res16  = (uint16_t)(*buf) << 8;           // Letztes Byte kommt in oberes Byte des Wortes
    res32 += res16;                           // Zu Summe hinzuaddieren
  }

  // Jetzt die einzelnen Worte der Summe 2x aufaddieren, so dass oberes Wort = 0 ist
  res32 = ((res32 & 0xFFFF0000) >> 16) + (res32 & 0x0000FFFF);
  res32 = ((res32 & 0xFFFF0000) >> 16) + (res32 & 0x0000FFFF);

  // Schließlich noch das Einerkomplement bilden
  res16 = ~(res32 & 0x0000FFFF);

  return res16;
}
