Files
86Box/src/network/netswitch.h
Jasmine Iwanek 2dc28d39b1 Network Switch support
Co-Authored-By: Alexander Babikov <2708460+lemondrops@users.noreply.github.com>
Co-Authored-By: cold-brewed <47337035+cold-brewed@users.noreply.github.com>
2025-08-02 16:18:09 -04:00

298 lines
8.7 KiB
C

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Network Switch backend
*
*
*
* Authors: cold-brewed
*
* Copyright 2024 cold-brewed
*/
#ifndef NET_SWITCH_H
#define NET_SWITCH_H
#ifdef _WIN32
#include <Winsock2.h> // before Windows.h, else Winsock 1 conflict
#include <Ws2tcpip.h> // needed for ip_mreq definition for multicast
#include <Windows.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#endif
#include <unistd.h>
#include "pb.h"
#include "networkmessage.pb.h"
/* Local switch multicast port */
#define NET_SWITCH_MULTICAST_PORT 8086
/* Remote switch connect port */
#define NET_SWITCH_REMOTE_PORT 8088
/* Remove switch. This offset is where the local source ports will begin. */
#define NET_SWITCH_RECV_PORT_OFFSET 198
/* Multicast group (IP Address) maximum length. String representation. */
#define MAX_MCAST_GROUP_LEN 32
/* The buffer length used for both receiving on sockets and protobuf serialize / deserialize */
#define NET_SWITCH_BUFFER_LENGTH 2048
/* Any frame above this size gets fragmented */
#define MAX_FRAME_SEND_SIZE 1200
/* Minimum fragment size we'll accept */
#define MIN_FRAG_RECV_SIZE 12
/*
Size of the fragment buffer - how many can we hold?
Note: FRAGMENT_BUFFER_LENGTH * MIN_FRAG_RECV_SIZE *must* be greater
than NET_MAX_FRAME or bad things will happen with large packets!
*/
#define FRAGMENT_BUFFER_LENGTH 128
/* Maximum number of switch groups */
#define MAX_SWITCH_GROUP 31
/* Size of a mac address in bytes. Used for the protobuf serializing / deserializing */
#define PB_MAC_ADDR_SIZE 6
/* This will define the version in use and the minimum required for communication */
#define NS_PROTOCOL_VERSION 1
/* Maximum string size for a printable (formatted) mac address */
#define MAX_PRINTABLE_MAC 32
/* Maximum hostname length for a remote switch host */
#define MAX_HOSTNAME 128
typedef enum {
FLAGS_NONE = 0,
FLAGS_PROMISC = 1 << 0,
} ns_flags_t;
typedef enum {
SWITCH_TYPE_LOCAL = 0,
SWITCH_TYPE_REMOTE,
} ns_type_t;
typedef enum {
DISCONNECTED,
CONNECTING,
CONNECTED,
LOCAL,
} ns_client_state_t;
struct ns_open_args {
uint8_t group;
ns_flags_t flags;
ns_type_t type;
char *client_id;
uint8_t mac_addr[6];
char nrs_hostname[MAX_HOSTNAME];
};
struct nsconn;
typedef struct nsconn NSCONN;
struct ns_stats {
size_t max_tx_frame;
size_t max_tx_packet;
size_t max_rx_frame;
size_t max_rx_packet;
uint8_t last_tx_ethertype[2];
uint8_t last_rx_ethertype[2];
u_long total_rx_packets;
u_long total_tx_packets;
u_long total_fragments;
uint8_t max_vec;
};
typedef struct {
/* The ID of the fragment. All fragments in a set should have the same ID. */
uint32_t id;
/* The fragment index in the sequence of fragments. NOTE: one indexed, not zero!
* Example: the first fragment of three would be 1 in the sequence */
uint32_t sequence;
/* Total number of fragments for the collection */
uint32_t total;
/* The sequence number of the packet that delivered the fragment. Not the same as fragment sequence above! */
uint32_t packet_sequence;
/* Frame data */
char *data;
/* Frame size. A size of zero indicates an unused fragment slot and unallocated data field. */
uint32_t size;
/* Epoch time (in ms) that the fragment is valid until */
uint64_t ttl;
} ns_fragment_t;
struct nsconn {
uint16_t flags;
int fdctl;
int fddata;
int fdout;
char mcast_group[MAX_MCAST_GROUP_LEN];
struct sockaddr_in addr;
struct sockaddr_in outaddr;
size_t outlen;
struct sockaddr *sock;
struct sockaddr *outsock;
struct ns_stats stats;
uint32_t client_id;
uint8_t mac_addr[6];
uint16_t sequence;
uint16_t remote_sequence;
uint8_t version;
uint8_t switch_type;
ns_client_state_t client_state;
int64_t last_packet_stamp;
/* Remote switch hostname */
char nrs_hostname[MAX_HOSTNAME];
/* Remote connect port for remote network switch */
uint16_t remote_network_port;
/* Local multicast port for the local network switch */
uint16_t local_multicast_port;
/*
* The source port to receive packets. Only applies to remote mode.
* This will also be the source port for sent packets in order to aid
* NAT
*/
uint16_t remote_source_port;
ns_fragment_t *fragment_buffer[FRAGMENT_BUFFER_LENGTH];
};
typedef struct {
uint32_t id;
uint32_t history;
} ns_ack_t;
typedef struct {
uint32_t id;
uint32_t sequence;
uint32_t total;
} ns_fragment_info_t;
typedef struct {
netpkt_t pkt;
MessageType type;
uint32_t client_id;
uint8_t mac[6];
uint32_t flags;
int64_t timestamp;
ns_ack_t ack;
ns_fragment_info_t fragment;
uint32_t version;
} ns_rx_packet_t;
typedef struct {
size_t size;
char src_mac_h[MAX_PRINTABLE_MAC];
char dest_mac_h[MAX_PRINTABLE_MAC];
char my_mac_h[MAX_PRINTABLE_MAC];
uint8_t src_mac[6];
uint8_t dest_mac[6];
bool is_packet_from_me;
bool is_broadcast;
bool is_packet_for_me;
bool is_data_packet;
char printable[128];
} data_packet_info_t;
typedef struct {
uint8_t src_mac[6];
char src_mac_h[MAX_PRINTABLE_MAC];
bool is_packet_from_me;
MessageType type;
char printable[128];
uint64_t client_id;
int64_t timestamp;
} control_packet_info_t;
/* Initializes and opens the Net Multicast Switch */
NSCONN *ns_open(struct ns_open_args *open_args);
/* Returns the flags */
int ns_flags(const NSCONN *conn);
/* Returns the file descriptor for polling */
int ns_pollfd(const NSCONN *conn);
/* This should be used to receive serialized protobuf packets
* and have the output placed in the packet struct */
bool ns_recv_pb(NSCONN *conn, ns_rx_packet_t *packet,size_t len,int flags);
/* Do not call directly! Used internally */
ssize_t ns_sock_recv(const NSCONN *conn,void *buf,size_t len,int flags);
/* This should be used to send serialized protobuf packets
* and have the output placed in the packet struct */
ssize_t ns_send_pb(NSCONN *conn, const netpkt_t *packet,int flags);
/* Send control messages */
bool ns_send_control(NSCONN *conn, MessageType type);
const char* ns_printable_message_type(MessageType type);
/* Do not call directly! Used internally */
ssize_t ns_sock_send(NSCONN *conn,const void *buf,size_t len,int flags);
uint32_t ns_gen_client_id(void);
/* Closes and cleans up */
int ns_close(NSCONN *conn);
/* Return current time in milliseconds */
int64_t ns_get_current_millis(void);
/* Is the packet a control packet?
* Any type other than DATA is a control packet, including fragments */
bool is_control_packet(const ns_rx_packet_t *packet);
/* Logic for handling control packets */
bool process_control_packet(NSCONN *conn, const ns_rx_packet_t *packet);
/* Is the packet a fragment packet? */
bool is_fragment_packet(const ns_rx_packet_t *packet);
/* Store a fragment in the fragment buffer */
bool store_fragment(const NSCONN *conn, const NetworkMessage *network_message);
/* Reassemble a fragment from the fragment buffer */
bool reassemble_fragment(const NSCONN *conn, netpkt_t *pkt, uint32_t packet_count);
/* Set up the socket. Accounts for the differences between local and remote modes */
bool ns_socket_setup(NSCONN *conn);
/* Is the switch in a connected state? Always returns true in local mode */
bool ns_connected(const NSCONN *conn);
/* Return a string with a properly formatted mac address.
* Note: Caller must free! */
char* formatted_mac(uint8_t mac_addr[6]);
/* Used for control packet info and logic */
control_packet_info_t get_control_packet_info(ns_rx_packet_t packet, const uint8_t *my_mac);
/* Used for data packet info and logic */
data_packet_info_t get_data_packet_info(const netpkt_t *packet, const uint8_t *my_mac);
/* Checks for a valid file descriptor */
bool fd_valid(int fd);
/* Wrapping increment for the sequence number */
bool seq_increment(NSCONN *conn);
#ifdef ENABLE_NET_SWITCH_LOG
static void
net_switch_log(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
#else
# define net_switch_log(fmt, ...)
#endif
#endif