/////////////////////////////////////////////////////////////////////////
// $Id$
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2004-2021  The Bochs Project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library 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
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
//

//  netutil.h  - shared code for eth_vnet.cc and bxhub.cc

#ifndef BX_NETUTIL_H
#define BX_NETUTIL_H

#define ETHERNET_MAC_ADDR_LEN 6

#if defined(_MSC_VER)
#pragma pack(push, 1)
#elif defined(__MWERKS__) && defined(macintosh)
#pragma options align=packed
#endif

typedef struct ethernet_header {
#if defined(_MSC_VER) && (_MSC_VER>=1300)
  __declspec(align(1))
#endif
  Bit8u  dst_mac_addr[ETHERNET_MAC_ADDR_LEN];
  Bit8u  src_mac_addr[ETHERNET_MAC_ADDR_LEN];
  Bit16u type;
}
#if !defined(_MSC_VER)
  GCC_ATTRIBUTE((packed))
#endif
ethernet_header_t;

typedef struct arp_header {
#if defined(_MSC_VER) && (_MSC_VER>=1300)
  __declspec(align(1))
#endif
  Bit16u  hw_addr_space;
  Bit16u  proto_addr_space;
  Bit8u   hw_addr_len;
  Bit8u   proto_addr_len;
  Bit16u  opcode;
  /* HW address of sender */
  /* Protocol address of sender */
  /* HW address of target*/
  /* Protocol address of target */
}
#if !defined(_MSC_VER)
  GCC_ATTRIBUTE((packed))
#endif
arp_header_t;

typedef struct ip_header {
#if defined(_MSC_VER) && (_MSC_VER>=1300)
  __declspec(align(1))
#endif
#ifdef BX_LITTLE_ENDIAN
  Bit8u header_len : 4;
  Bit8u version : 4;
#else
  Bit8u version : 4;
  Bit8u header_len : 4;
#endif
  Bit8u tos;
  Bit16u total_len;
  Bit16u id;
  Bit16u frag_offs;
  Bit8u ttl;
  Bit8u protocol;
  Bit16u checksum;
  Bit8u src_addr[4];
  Bit8u dst_addr[4];
}
#if !defined(_MSC_VER)
  GCC_ATTRIBUTE((packed))
#endif
ip_header_t;

typedef struct udp_header {
#if defined(_MSC_VER) && (_MSC_VER>=1300)
  __declspec(align(1))
#endif
  Bit16u src_port;
  Bit16u dst_port;
  Bit16u length;
  Bit16u checksum;
}
#if !defined(_MSC_VER)
  GCC_ATTRIBUTE((packed))
#endif
udp_header_t;

typedef struct tcp_header {
#if defined(_MSC_VER) && (_MSC_VER>=1300)
  __declspec(align(1))
#endif
  Bit16u src_port;
  Bit16u dst_port;
  Bit32u seq_num;
  Bit32u ack_num;
#ifdef BX_LITTLE_ENDIAN
  Bit8u reserved : 4;
  Bit8u data_offset : 4;
#else
  Bit8u data_offset : 4;
  Bit8u reserved : 4;
#endif
  struct {
#ifdef BX_LITTLE_ENDIAN
    Bit8u fin : 1;
    Bit8u syn : 1;
    Bit8u rst : 1;
    Bit8u psh : 1;
    Bit8u ack : 1;
    Bit8u urg : 1;
    Bit8u ece : 1;
    Bit8u cwr : 1;
#else
    Bit8u cwr : 1;
    Bit8u ece : 1;
    Bit8u urg : 1;
    Bit8u ack : 1;
    Bit8u psh : 1;
    Bit8u rst : 1;
    Bit8u syn : 1;
    Bit8u fin : 1;
#endif
  } flags;
  Bit16u window;
  Bit16u checksum;
  Bit16u urgent_ptr;
}
#if !defined(_MSC_VER)
  GCC_ATTRIBUTE((packed))
#endif
tcp_header_t;

#if defined(_MSC_VER)
#pragma pack(pop)
#elif defined(__MWERKS__) && defined(macintosh)
#pragma options align=reset
#endif

// VNET service IDs
#define VNET_SRV  0
#define VNET_DNS  1
#define VNET_MISC 2

// DHCP configuration structure
typedef struct {
  Bit8u host_macaddr[6];
  Bit8u net_ipv4addr[4];
  Bit8u srv_ipv4addr[3][4];
  Bit8u client_base_ipv4addr[4];
  char  bootfile[128];
} dhcp_cfg_t;

// vnet functions shared with bxhub
#ifdef BXHUB
#define logfunctions void
#endif
Bit16u ip_checksum(const Bit8u *buf, unsigned buf_len);

typedef struct tftp_session {
  char     filename[BX_PATHNAME_LEN];
  Bit16u   tid;
  bool     iswrite;
  unsigned options;
  size_t   tsize_val;
  unsigned blksize_val;
  unsigned timeout_val;
  unsigned timestamp;
  struct tftp_session *next;
} tftp_session_t;

// VNET server

#define VNET_MAX_CLIENTS 6
#define LAYER4_LISTEN_MAX  128

typedef int (*layer4_handler_t)(
  void *this_ptr,
  const Bit8u *ipheader,
  unsigned ipheader_len,
  unsigned sourceport,
  unsigned targetport,
  const Bit8u *data,
  unsigned data_len,
  Bit8u *reply
  );

// dynamic packet handling

typedef struct packet_item {
  Bit8u *buffer;
  unsigned len;
  struct packet_item *next;
} packet_item_t;

// TCP support

typedef struct tcp_conn {
  Bit8u  clientid;
  Bit16u src_port;
  Bit16u dst_port;
  Bit32u host_seq_num;
  Bit32u guest_seq_num;
  Bit16u window;
  Bit8u  state;
  bool   host_port_fin;
  void   *data;
  struct tcp_conn *next;
} tcp_conn_t;

typedef void (*tcp_handler_t)(
  void *this_ptr,
  tcp_conn_t *tcp_conn,
  const Bit8u *data,
  unsigned data_len
  );


class vnet_server_c {
public:
  vnet_server_c();
  virtual ~vnet_server_c();

  void init(logfunctions *netdev, dhcp_cfg_t *dhcpc, const char *tftp_rootdir);
  void init_client(Bit8u clientid, const Bit8u *macaddr, char *hostname);
  void handle_packet(const Bit8u *buf, unsigned len);
  unsigned get_packet(Bit8u *buf);
#ifdef BXHUB
  void init_log(const char *logfn);
#endif

  layer4_handler_t get_layer4_handler(unsigned ipprotocol, unsigned port);
  bool register_layer4_handler(unsigned ipprotocol, unsigned port,
                               layer4_handler_t func);
  bool unregister_layer4_handler(unsigned ipprotocol, unsigned port);

  tcp_handler_t get_tcp_handler(unsigned port);
  bool register_tcp_handler(unsigned port, tcp_handler_t func);
  bool unregister_tcp_handler(unsigned port);

private:
#ifdef BXHUB
  void bx_printf(const char *fmt, ...);
#endif
  bool find_client(const Bit8u *mac_addr, Bit8u *clientid);

  void host_to_guest(Bit8u clientid, Bit8u *buf, unsigned len, unsigned l3type);

  void process_arp(Bit8u clientid, const Bit8u *buf, unsigned len);

  void process_ipv4(Bit8u clientid, const Bit8u *buf, unsigned len);
  void host_to_guest_ipv4(Bit8u clientid, Bit8u srv_id, Bit8u *buf, unsigned len);

  void process_icmpipv4(Bit8u clientid, Bit8u srv_id, const Bit8u *ipheader,
                        unsigned ipheader_len, const Bit8u *l4pkt, unsigned l4pkt_len);
  void process_tcpipv4(Bit8u clientid, Bit8u srv_id, const Bit8u *ipheader,
                       unsigned ipheader_len, const Bit8u *l4pkt, unsigned l4pkt_len);
  void process_udpipv4(Bit8u clientid, Bit8u srv_id, const Bit8u *ipheader,
                       unsigned ipheader_len, const Bit8u *l4pkt, unsigned l4pkt_len);

  void host_to_guest_tcpipv4(Bit8u clientid, Bit8u srv_id, Bit16u src_port,
                             Bit16u dst_port, Bit8u *data, unsigned data_len,
                             unsigned hdr_len);
  unsigned tcpipv4_send_data(tcp_conn_t *tcp_conn, const Bit8u *data,
                             unsigned data_len, bool push);
  void tcpipv4_send_ack(tcp_conn_t *tcp_conn, unsigned data_len);
  void tcpipv4_send_fin(tcp_conn_t *tcp_conn, bool host_fin);

  static void tcpipv4_ftp_handler(void *this_ptr, tcp_conn_t *tcp_conn,
                                  const Bit8u *data, unsigned data_len);
  void tcpipv4_ftp_handler_ns(tcp_conn_t *tcp_conn, const Bit8u *data,
                              unsigned data_len);
  bool ftp_file_exists(tcp_conn_t *tcpc_cmd, const char *arg, char *path,
                       unsigned *size);
  bool ftp_subdir_exists(tcp_conn_t *tcpc_cmd, const char *arg, char *path);
  void ftp_send_reply(tcp_conn_t *tcp_conn, const char *msg);
  void ftp_send_status(tcp_conn_t *tcp_conn);
  void ftp_send_data_prep(tcp_conn_t *tcpc_cmd, tcp_conn_t *tcpc_data,
                          const char *path, unsigned data_len);
  void ftp_send_data(tcp_conn_t *tcpc_cmd, tcp_conn_t *tcpc_data);
  void ftp_list_directory(tcp_conn_t *tcpc_cmd, tcp_conn_t *tcpc_data,
                          const char *options, const char *subdir);
  void ftp_recv_file(tcp_conn_t *tcpc_cmd, tcp_conn_t *tcpc_data, const char *fname);
  void ftp_send_file(tcp_conn_t *tcpc_cmd, tcp_conn_t *tcpc_data, const char *arg);
  void ftp_get_filesize(tcp_conn_t *tcp_conn, const char *arg);

  static int udpipv4_dhcp_handler(void *this_ptr, const Bit8u *ipheader,
                                  unsigned ipheader_len, unsigned sourceport,
                                  unsigned targetport, const Bit8u *data,
                                  unsigned data_len, Bit8u *reply);
  int udpipv4_dhcp_handler_ns(const Bit8u *ipheader, unsigned ipheader_len,
                              unsigned sourceport, unsigned targetport,
                              const Bit8u *data, unsigned data_len, Bit8u *reply);
  static int udpipv4_tftp_handler(void *this_ptr, const Bit8u *ipheader,
                                  unsigned ipheader_len, unsigned sourceport,
                                  unsigned targetport, const Bit8u *data,
                                  unsigned data_len, Bit8u *reply);
  int udpipv4_tftp_handler_ns(const Bit8u *ipheader, unsigned ipheader_len,
                              unsigned sourceport, unsigned targetport,
                              const Bit8u *data, unsigned data_len, Bit8u *reply);
  static int udpipv4_dns_handler(void *this_ptr, const Bit8u *ipheader,
                                 unsigned ipheader_len, unsigned sourceport,
                                 unsigned targetport, const Bit8u *data,
                                 unsigned data_len, Bit8u *reply);
  int udpipv4_dns_handler_ns(const Bit8u *ipheader, unsigned ipheader_len,
                             unsigned sourceport, unsigned targetport,
                             const Bit8u *data, unsigned data_len, Bit8u *reply);

  void tftp_parse_options(const char *mode, const Bit8u *data, unsigned data_len,
                          tftp_session_t *s);

#ifdef BXHUB
  FILE *logfd;
#endif
  logfunctions *netdev;
  dhcp_cfg_t *dhcp;
  const char *tftp_root;

  struct {
    bool init;
    const Bit8u *macaddr;
    Bit8u default_ipv4addr[4];
    Bit8u ipv4addr[4];
    char *hostname;
  } client[VNET_MAX_CLIENTS];

  struct {
    unsigned ipprotocol;
    unsigned port;
    layer4_handler_t func;
  } l4data[LAYER4_LISTEN_MAX];

  struct {
    unsigned port;
    tcp_handler_t func;
  } tcpfn[LAYER4_LISTEN_MAX];

  unsigned l4data_used;
  unsigned tcpfn_used;

  Bit16u packet_counter;
  packet_item_t *packets;
};

#endif
