Added basic TCP handling and FTP server stub for testing.
- Partly re-implemented host_to_guest methods from legacy eth_vnet.cc. - Added two dynamic packet buffers for vnet server replies. - TFTP and FTP servers are only available if root directory is configured. - Added TCP port handler methods similar to the UDP ones. - Added basic TCP connection management and 3-way handshake. - Added FTP server stub for testing TCP (only login/logout for now).
This commit is contained in:
parent
1b0845b48a
commit
9c3a99856e
@ -87,6 +87,7 @@ public:
|
|||||||
void sendpkt(void *buf, unsigned io_len);
|
void sendpkt(void *buf, unsigned io_len);
|
||||||
private:
|
private:
|
||||||
void guest_to_host(const Bit8u *buf, unsigned io_len);
|
void guest_to_host(const Bit8u *buf, unsigned io_len);
|
||||||
|
void host_to_guest(void);
|
||||||
|
|
||||||
vnet_server_c vnet_server;
|
vnet_server_c vnet_server;
|
||||||
|
|
||||||
@ -214,10 +215,16 @@ void bx_vnet_pktmover_c::guest_to_host(const Bit8u *buf, unsigned io_len)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->tx_time = (64 + 96 + 4 * 8 + io_len * 8) / this->netdev_speed;
|
this->tx_time = (64 + 96 + 4 * 8 + io_len * 8) / this->netdev_speed;
|
||||||
packet_len = vnet_server.handle_packet(buf, io_len, packet_buffer);
|
vnet_server.handle_packet(buf, io_len);
|
||||||
|
|
||||||
|
host_to_guest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bx_vnet_pktmover_c::host_to_guest(void)
|
||||||
|
{
|
||||||
|
packet_len = vnet_server.get_packet(packet_buffer);
|
||||||
if (packet_len > 0) {
|
if (packet_len > 0) {
|
||||||
// host-to-guest
|
unsigned rx_time = (64 + 96 + 4 * 8 + packet_len * 8) / this->netdev_speed;
|
||||||
unsigned rx_time = (64 + 96 + 4 * 8 + io_len * 8) / this->netdev_speed;
|
|
||||||
bx_pc_system.activate_timer(this->rx_timer_index, this->tx_time + rx_time + 100, 0);
|
bx_pc_system.activate_timer(this->rx_timer_index, this->tx_time + rx_time + 100, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,6 +255,8 @@ void bx_vnet_pktmover_c::rx_timer(void)
|
|||||||
fflush((FILE *)pktlog_pcap);
|
fflush((FILE *)pktlog_pcap);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
// check for another pending packet
|
||||||
|
host_to_guest();
|
||||||
} else {
|
} else {
|
||||||
BX_ERROR(("device not ready to receive data"));
|
BX_ERROR(("device not ready to receive data"));
|
||||||
}
|
}
|
||||||
|
@ -173,13 +173,10 @@ vnet_server_c::vnet_server_c()
|
|||||||
#endif
|
#endif
|
||||||
l4data_used = 0;
|
l4data_used = 0;
|
||||||
|
|
||||||
register_layer4_handler(0x11, INET_PORT_BOOTP_SERVER, udpipv4_dhcp_handler);
|
|
||||||
register_layer4_handler(0x11, INET_PORT_TFTP_SERVER, udpipv4_tftp_handler);
|
|
||||||
register_layer4_handler(0x11, INET_PORT_DOMAIN, udpipv4_dns_handler);
|
|
||||||
|
|
||||||
for (Bit8u c = 0; c < VNET_MAX_CLIENTS; c++) {
|
for (Bit8u c = 0; c < VNET_MAX_CLIENTS; c++) {
|
||||||
client[c].init = 0;
|
client[c].init = 0;
|
||||||
}
|
}
|
||||||
|
packet_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
vnet_server_c::~vnet_server_c()
|
vnet_server_c::~vnet_server_c()
|
||||||
@ -203,6 +200,13 @@ void vnet_server_c::init(bx_devmodel_c *_netdev, dhcp_cfg_t *dhcpc, const char *
|
|||||||
dhcp = dhcpc;
|
dhcp = dhcpc;
|
||||||
memcpy(broadcast_ipv4addr[2], dhcp->host_ipv4addr, 3);
|
memcpy(broadcast_ipv4addr[2], dhcp->host_ipv4addr, 3);
|
||||||
tftp_root = tftp_rootdir;
|
tftp_root = tftp_rootdir;
|
||||||
|
|
||||||
|
register_layer4_handler(0x11, INET_PORT_BOOTP_SERVER, udpipv4_dhcp_handler);
|
||||||
|
register_layer4_handler(0x11, INET_PORT_DOMAIN, udpipv4_dns_handler);
|
||||||
|
if (strlen(tftp_root) > 0) {
|
||||||
|
register_layer4_handler(0x11, INET_PORT_TFTP_SERVER, udpipv4_tftp_handler);
|
||||||
|
register_tcp_handler(INET_PORT_FTP, tcpipv4_ftp_handler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vnet_server_c::init_client(Bit8u clientid, const Bit8u *macaddr, const Bit8u *default_ipv4addr)
|
void vnet_server_c::init_client(Bit8u clientid, const Bit8u *macaddr, const Bit8u *default_ipv4addr)
|
||||||
@ -248,55 +252,87 @@ bx_bool vnet_server_c::find_client(const Bit8u *mac_addr, Bit8u *clientid)
|
|||||||
return (*clientid < VNET_MAX_CLIENTS);
|
return (*clientid < VNET_MAX_CLIENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
int vnet_server_c::handle_packet(const Bit8u *buf, unsigned len, Bit8u *reply)
|
void vnet_server_c::handle_packet(const Bit8u *buf, unsigned len)
|
||||||
{
|
{
|
||||||
ethernet_header_t *ethhdr = (ethernet_header_t *)buf;
|
ethernet_header_t *ethhdr = (ethernet_header_t *)buf;
|
||||||
Bit8u clientid = 0xff;
|
Bit8u clientid = 0xff;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (len >= 14) {
|
if (len >= 14) {
|
||||||
if (!find_client(ethhdr->src_mac_addr, &clientid))
|
if (!find_client(ethhdr->src_mac_addr, &clientid))
|
||||||
return ret;
|
return;
|
||||||
if (!memcmp(ðhdr->dst_mac_addr, dhcp->host_macaddr, 6) ||
|
if (!memcmp(ðhdr->dst_mac_addr, dhcp->host_macaddr, 6) ||
|
||||||
!memcmp(ðhdr->dst_mac_addr, &broadcast_macaddr[0], 6)) {
|
!memcmp(ðhdr->dst_mac_addr, &broadcast_macaddr[0], 6)) {
|
||||||
switch (ntohs(ethhdr->type)) {
|
switch (ntohs(ethhdr->type)) {
|
||||||
case ETHERNET_TYPE_IPV4:
|
case ETHERNET_TYPE_IPV4:
|
||||||
ret = process_ipv4((Bit8u)clientid, buf, len, reply);
|
process_ipv4(clientid, buf, len);
|
||||||
break;
|
break;
|
||||||
case ETHERNET_TYPE_ARP:
|
case ETHERNET_TYPE_ARP:
|
||||||
ret = process_arp((Bit8u)clientid, buf, len, reply);
|
process_arp(clientid, buf, len);
|
||||||
break;
|
break;
|
||||||
default: // unknown packet type.
|
default: // unknown packet type.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ret > 0) {
|
|
||||||
if (ret < 14) {
|
|
||||||
BX_ERROR(("host_to_guest: io_len < 14!"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (ret < MIN_RX_PACKET_LEN) {
|
|
||||||
ret = MIN_RX_PACKET_LEN;
|
|
||||||
}
|
|
||||||
ethernet_header_t *ethrhdr = (ethernet_header_t *)reply;
|
|
||||||
memcpy(ethrhdr->dst_mac_addr, ethhdr->src_mac_addr, ETHERNET_MAC_ADDR_LEN);
|
|
||||||
memcpy(ethrhdr->src_mac_addr, dhcp->host_macaddr, ETHERNET_MAC_ADDR_LEN);
|
|
||||||
ethrhdr->type = ethhdr->type;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int vnet_server_c::process_arp(Bit8u clientid, const Bit8u *buf, unsigned len, Bit8u *reply)
|
unsigned vnet_server_c::get_packet(Bit8u *buf)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
unsigned len = 0;
|
||||||
|
|
||||||
if (len < 22) return 0;
|
if (packet_count > 0) {
|
||||||
if (len < (unsigned)(22+buf[18]*2+buf[19]*2)) return 0;
|
len = packet_len[0];
|
||||||
|
memcpy(buf, packet_buffer[0], len);
|
||||||
|
packet_len[0] = 0;
|
||||||
|
delete [] packet_buffer[0];
|
||||||
|
if (packet_count > 1) {
|
||||||
|
packet_buffer[0] = packet_buffer[1];
|
||||||
|
packet_len[0] = packet_len[1];
|
||||||
|
} else {
|
||||||
|
packet_buffer[0] = NULL;
|
||||||
|
}
|
||||||
|
packet_count--;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vnet_server_c::host_to_guest(Bit8u clientid, Bit8u *buf, unsigned len, unsigned l3type)
|
||||||
|
{
|
||||||
|
if (len < 14) {
|
||||||
|
BX_ERROR(("host_to_guest: io_len < 14!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (len < MIN_RX_PACKET_LEN) {
|
||||||
|
len = MIN_RX_PACKET_LEN;
|
||||||
|
}
|
||||||
|
ethernet_header_t *ethrhdr = (ethernet_header_t *)buf;
|
||||||
|
memcpy(ethrhdr->dst_mac_addr, client[clientid].macaddr, ETHERNET_MAC_ADDR_LEN);
|
||||||
|
memcpy(ethrhdr->src_mac_addr, dhcp->host_macaddr, ETHERNET_MAC_ADDR_LEN);
|
||||||
|
ethrhdr->type = htons(l3type);
|
||||||
|
|
||||||
|
if (packet_count < 2) {
|
||||||
|
packet_buffer[packet_count] = new Bit8u[len];
|
||||||
|
memcpy(packet_buffer[packet_count], buf, len);
|
||||||
|
packet_len[packet_count++] = len;
|
||||||
|
} else {
|
||||||
|
BX_ERROR(("host_to_guest(): too many pending packets"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
// ARP
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void vnet_server_c::process_arp(Bit8u clientid, const Bit8u *buf, unsigned len)
|
||||||
|
{
|
||||||
|
Bit8u replybuf[MIN_RX_PACKET_LEN];
|
||||||
|
|
||||||
|
if (len < 22) return;
|
||||||
|
if (len < (unsigned)(22+buf[18]*2+buf[19]*2)) return;
|
||||||
|
|
||||||
arp_header_t *arphdr = (arp_header_t *)((Bit8u *)buf +
|
arp_header_t *arphdr = (arp_header_t *)((Bit8u *)buf +
|
||||||
sizeof(ethernet_header_t));
|
sizeof(ethernet_header_t));
|
||||||
arp_header_t *arprhdr = (arp_header_t *)(reply + sizeof(ethernet_header_t));
|
arp_header_t *arprhdr = (arp_header_t *)(replybuf + sizeof(ethernet_header_t));
|
||||||
|
|
||||||
if ((ntohs(arphdr->hw_addr_space) != 0x0001) ||
|
if ((ntohs(arphdr->hw_addr_space) != 0x0001) ||
|
||||||
(ntohs(arphdr->proto_addr_space) != 0x0800) ||
|
(ntohs(arphdr->proto_addr_space) != 0x0800) ||
|
||||||
@ -305,7 +341,7 @@ int vnet_server_c::process_arp(Bit8u clientid, const Bit8u *buf, unsigned len, B
|
|||||||
BX_ERROR(("Unhandled ARP message hw: 0x%04x (%d) proto: 0x%04x (%d)",
|
BX_ERROR(("Unhandled ARP message hw: 0x%04x (%d) proto: 0x%04x (%d)",
|
||||||
ntohs(arphdr->hw_addr_space), arphdr->hw_addr_len,
|
ntohs(arphdr->hw_addr_space), arphdr->hw_addr_len,
|
||||||
ntohs(arphdr->proto_addr_space), arphdr->proto_addr_len));
|
ntohs(arphdr->proto_addr_space), arphdr->proto_addr_len));
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ntohs(arphdr->opcode)) {
|
switch (ntohs(arphdr->opcode)) {
|
||||||
@ -314,14 +350,14 @@ int vnet_server_c::process_arp(Bit8u clientid, const Bit8u *buf, unsigned len, B
|
|||||||
memcpy(client[clientid].ipv4addr, &buf[28], 4);
|
memcpy(client[clientid].ipv4addr, &buf[28], 4);
|
||||||
if (!memcmp(&buf[38], dhcp->host_ipv4addr, 4) ||
|
if (!memcmp(&buf[38], dhcp->host_ipv4addr, 4) ||
|
||||||
!memcmp(&buf[38], dhcp->dns_ipv4addr, 4)) {
|
!memcmp(&buf[38], dhcp->dns_ipv4addr, 4)) {
|
||||||
memset(reply, 0, MIN_RX_PACKET_LEN);
|
memset(replybuf, 0, MIN_RX_PACKET_LEN);
|
||||||
memcpy(arprhdr, &buf[14], 6);
|
memcpy(arprhdr, &buf[14], 6);
|
||||||
put_net2((Bit8u*)&arprhdr->opcode, ARP_OPCODE_REPLY);
|
put_net2((Bit8u*)&arprhdr->opcode, ARP_OPCODE_REPLY);
|
||||||
memcpy((Bit8u *)arprhdr+8, dhcp->host_macaddr, ETHERNET_MAC_ADDR_LEN);
|
memcpy((Bit8u *)arprhdr+8, dhcp->host_macaddr, ETHERNET_MAC_ADDR_LEN);
|
||||||
memcpy((Bit8u *)arprhdr+14, &buf[38], 4);
|
memcpy((Bit8u *)arprhdr+14, &buf[38], 4);
|
||||||
memcpy((Bit8u *)arprhdr+18, client[clientid].macaddr, ETHERNET_MAC_ADDR_LEN);
|
memcpy((Bit8u *)arprhdr+18, client[clientid].macaddr, ETHERNET_MAC_ADDR_LEN);
|
||||||
memcpy((Bit8u *)arprhdr+24, client[clientid].ipv4addr, 4);
|
memcpy((Bit8u *)arprhdr+24, client[clientid].ipv4addr, 4);
|
||||||
ret = MIN_RX_PACKET_LEN;
|
host_to_guest(clientid, replybuf, MIN_RX_PACKET_LEN, ETHERNET_TYPE_ARP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -338,12 +374,14 @@ int vnet_server_c::process_arp(Bit8u clientid, const Bit8u *buf, unsigned len, B
|
|||||||
BX_ERROR(("arp: unknown ARP opcode 0x%04x", ntohs(arphdr->opcode)));
|
BX_ERROR(("arp: unknown ARP opcode 0x%04x", ntohs(arphdr->opcode)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int vnet_server_c::process_ipv4(Bit8u clientid, const Bit8u *buf, unsigned len, Bit8u *reply)
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
// IPv4
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void vnet_server_c::process_ipv4(Bit8u clientid, const Bit8u *buf, unsigned len)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
|
||||||
unsigned total_len;
|
unsigned total_len;
|
||||||
unsigned fragment_flags;
|
unsigned fragment_flags;
|
||||||
unsigned fragment_offset;
|
unsigned fragment_offset;
|
||||||
@ -355,23 +393,23 @@ int vnet_server_c::process_ipv4(Bit8u clientid, const Bit8u *buf, unsigned len,
|
|||||||
|
|
||||||
if (len < (14U+20U)) {
|
if (len < (14U+20U)) {
|
||||||
BX_ERROR(("ip packet - too small packet"));
|
BX_ERROR(("ip packet - too small packet"));
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ip_header_t *iphdr = (ip_header_t *)((Bit8u *)buf +
|
ip_header_t *iphdr = (ip_header_t *)((Bit8u *)buf +
|
||||||
sizeof(ethernet_header_t));
|
sizeof(ethernet_header_t));
|
||||||
if (iphdr->version != 4) {
|
if (iphdr->version != 4) {
|
||||||
BX_ERROR(("ipv%u packet - not implemented", iphdr->version));
|
BX_ERROR(("ipv%u packet - not implemented", iphdr->version));
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
l3header_len = (iphdr->header_len << 2);
|
l3header_len = (iphdr->header_len << 2);
|
||||||
if (l3header_len != 20) {
|
if (l3header_len != 20) {
|
||||||
BX_ERROR(("ip: option header is not implemented"));
|
BX_ERROR(("ip: option header is not implemented"));
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
if (ip_checksum((Bit8u*)iphdr, l3header_len) != (Bit16u)0xffff) {
|
if (ip_checksum((Bit8u*)iphdr, l3header_len) != (Bit16u)0xffff) {
|
||||||
BX_ERROR(("ip: invalid checksum"));
|
BX_ERROR(("ip: invalid checksum"));
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
total_len = ntohs(iphdr->total_len);
|
total_len = ntohs(iphdr->total_len);
|
||||||
@ -391,7 +429,7 @@ int vnet_server_c::process_ipv4(Bit8u clientid, const Bit8u *buf, unsigned len,
|
|||||||
BX_ERROR(("target IP address %u.%u.%u.%u is unknown",
|
BX_ERROR(("target IP address %u.%u.%u.%u is unknown",
|
||||||
(unsigned)buf[14+16],(unsigned)buf[14+17],
|
(unsigned)buf[14+16],(unsigned)buf[14+17],
|
||||||
(unsigned)buf[14+18],(unsigned)buf[14+19]));
|
(unsigned)buf[14+18],(unsigned)buf[14+19]));
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment_flags = ntohs(iphdr->frag_offs) >> 13;
|
fragment_flags = ntohs(iphdr->frag_offs) >> 13;
|
||||||
@ -399,7 +437,7 @@ int vnet_server_c::process_ipv4(Bit8u clientid, const Bit8u *buf, unsigned len,
|
|||||||
|
|
||||||
if ((fragment_flags & 0x1) || (fragment_offset != 0)) {
|
if ((fragment_flags & 0x1) || (fragment_offset != 0)) {
|
||||||
BX_ERROR(("ignore fragmented packet!"));
|
BX_ERROR(("ignore fragmented packet!"));
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
l4pkt = &buf[14 + l3header_len];
|
l4pkt = &buf[14 + l3header_len];
|
||||||
@ -407,124 +445,433 @@ int vnet_server_c::process_ipv4(Bit8u clientid, const Bit8u *buf, unsigned len,
|
|||||||
|
|
||||||
switch (iphdr->protocol) {
|
switch (iphdr->protocol) {
|
||||||
case 0x01: // ICMP
|
case 0x01: // ICMP
|
||||||
ret = process_icmpipv4(clientid, &buf[14], l3header_len, l4pkt, l4pkt_len, reply);
|
process_icmpipv4(clientid, &buf[14], l3header_len, l4pkt, l4pkt_len);
|
||||||
break;
|
break;
|
||||||
case 0x06: // TCP
|
case 0x06: // TCP
|
||||||
ret = process_tcpipv4(clientid, &buf[14], l3header_len, l4pkt, l4pkt_len, reply);
|
process_tcpipv4(clientid, &buf[14], l3header_len, l4pkt, l4pkt_len);
|
||||||
break;
|
break;
|
||||||
case 0x11: // UDP
|
case 0x11: // UDP
|
||||||
ret = process_udpipv4(clientid, &buf[14], l3header_len, l4pkt, l4pkt_len, reply);
|
process_udpipv4(clientid, &buf[14], l3header_len, l4pkt, l4pkt_len);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BX_ERROR(("unknown IP protocol %02x", iphdr->protocol));
|
BX_ERROR(("unknown IP protocol %02x", iphdr->protocol));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ret > 0) {
|
|
||||||
// host-to-guest IPv4
|
|
||||||
reply[14] = (reply[14] & 0x0f) | 0x40;
|
|
||||||
l3header_len = ((unsigned)(reply[14] & 0x0f) << 2);
|
|
||||||
memcpy(&reply[26], srv_ipv4addr, 4);
|
|
||||||
memcpy(&reply[30], client[clientid].ipv4addr, 4);
|
|
||||||
put_net2(&reply[24], 0);
|
|
||||||
put_net2(&reply[24], ip_checksum(&reply[14], l3header_len) ^ (Bit16u)0xffff);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int vnet_server_c::process_icmpipv4(Bit8u clientid, const Bit8u *ipheader,
|
void vnet_server_c::host_to_guest_ipv4(const Bit8u clientid, bx_bool dns_srv, Bit8u *buf, unsigned len)
|
||||||
unsigned ipheader_len, const Bit8u *l4pkt,
|
{
|
||||||
unsigned l4pkt_len, Bit8u *reply)
|
unsigned l3header_len;
|
||||||
|
|
||||||
|
buf[14] = (buf[14] & 0x0f) | 0x40;
|
||||||
|
l3header_len = ((unsigned)(buf[14] & 0x0f) << 2);
|
||||||
|
if (dns_srv) {
|
||||||
|
memcpy(&buf[26], dhcp->dns_ipv4addr, 4);
|
||||||
|
} else {
|
||||||
|
memcpy(&buf[26], dhcp->host_ipv4addr, 4);
|
||||||
|
}
|
||||||
|
memcpy(&buf[30], client[clientid].ipv4addr, 4);
|
||||||
|
put_net2(&buf[24], 0);
|
||||||
|
put_net2(&buf[24], ip_checksum(&buf[14], l3header_len) ^ (Bit16u)0xffff);
|
||||||
|
|
||||||
|
host_to_guest(clientid, buf, len, ETHERNET_TYPE_IPV4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
// ICMP/IPv4
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void vnet_server_c::process_icmpipv4(Bit8u clientid, const Bit8u *ipheader,
|
||||||
|
unsigned ipheader_len, const Bit8u *l4pkt,
|
||||||
|
unsigned l4pkt_len)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
|
||||||
unsigned icmptype;
|
unsigned icmptype;
|
||||||
unsigned icmpcode;
|
unsigned icmpcode;
|
||||||
|
Bit8u replybuf[ICMP_ECHO_PACKET_MAX];
|
||||||
|
|
||||||
if (l4pkt_len < 8) return 0;
|
if (l4pkt_len < 8) return;
|
||||||
icmptype = l4pkt[0];
|
icmptype = l4pkt[0];
|
||||||
icmpcode = l4pkt[1];
|
icmpcode = l4pkt[1];
|
||||||
if (ip_checksum(l4pkt, l4pkt_len) != (Bit16u)0xffff) {
|
if (ip_checksum(l4pkt, l4pkt_len) != (Bit16u)0xffff) {
|
||||||
BX_ERROR(("icmp: invalid checksum"));
|
BX_ERROR(("icmp: invalid checksum"));
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (icmptype) {
|
switch (icmptype) {
|
||||||
case 0x08: // ECHO
|
case 0x08: // ECHO
|
||||||
if (icmpcode == 0) {
|
if (icmpcode == 0) {
|
||||||
if ((14U+ipheader_len+l4pkt_len) > ICMP_ECHO_PACKET_MAX) {
|
if ((14U+ipheader_len+l4pkt_len) > ICMP_ECHO_PACKET_MAX) {
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
memcpy(&reply[14], ipheader, ipheader_len);
|
memcpy(&replybuf[14], ipheader, ipheader_len);
|
||||||
memcpy(&reply[14+ipheader_len], l4pkt, l4pkt_len);
|
memcpy(&replybuf[14+ipheader_len], l4pkt, l4pkt_len);
|
||||||
reply[14+ipheader_len+0] = 0x00; // echo reply
|
replybuf[14+ipheader_len+0] = 0x00; // echo reply
|
||||||
put_net2(&reply[14+ipheader_len+2],0);
|
put_net2(&replybuf[14+ipheader_len+2],0);
|
||||||
put_net2(&reply [14+ipheader_len+2],
|
put_net2(&replybuf[14+ipheader_len+2],
|
||||||
ip_checksum(&reply[14+ipheader_len],l4pkt_len) ^ (Bit16u)0xffff);
|
ip_checksum(&replybuf[14+ipheader_len],l4pkt_len) ^ (Bit16u)0xffff);
|
||||||
ret = ICMP_ECHO_PACKET_MAX;
|
host_to_guest_ipv4(clientid, 0, replybuf, 14U+ipheader_len+l4pkt_len);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BX_ERROR(("unhandled icmp packet: type=%u code=%u", icmptype, icmpcode));
|
BX_ERROR(("unhandled icmp packet: type=%u code=%u", icmptype, icmpcode));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int vnet_server_c::process_tcpipv4(Bit8u clientid, const Bit8u *ipheader,
|
/////////////////////////////////////////////////////////////////////////
|
||||||
unsigned ipheader_len, const Bit8u *l4pkt,
|
// TCP/IPv4
|
||||||
unsigned l4pkt_len, Bit8u *reply)
|
/////////////////////////////////////////////////////////////////////////
|
||||||
{
|
|
||||||
unsigned tcp_src_port, tcp_dst_port, tcphdr_len, tcpdata_len = 0;
|
|
||||||
Bit32u guest_seq_num;
|
|
||||||
static Bit32u host_seq_num = 0;
|
|
||||||
|
|
||||||
if (l4pkt_len < 20) return 0;
|
#define TCP_DISCONNECTED 0
|
||||||
|
#define TCP_CONNECTING 1
|
||||||
|
#define TCP_CONNECTED 2
|
||||||
|
#define TCP_DISCONNECTING 3
|
||||||
|
|
||||||
|
// TCP handler methods
|
||||||
|
|
||||||
|
tcp_handler_t vnet_server_c::get_tcp_handler(unsigned port)
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
|
||||||
|
for (n = 0; n < tcpfn_used; n++) {
|
||||||
|
if (tcpfn[n].port == port)
|
||||||
|
return tcpfn[n].func;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tcp_handler_t)NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bx_bool vnet_server_c::register_tcp_handler(unsigned port, tcp_handler_t func)
|
||||||
|
{
|
||||||
|
if (get_tcp_handler(port) != (tcp_handler_t)NULL) {
|
||||||
|
BX_ERROR(("TCP port %u is already in use", port));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned n;
|
||||||
|
|
||||||
|
for (n = 0; n < tcpfn_used; n++) {
|
||||||
|
if (tcpfn[n].func == (tcp_handler_t)NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == tcpfn_used) {
|
||||||
|
if (n >= LAYER4_LISTEN_MAX) {
|
||||||
|
BX_ERROR(("vnet: LAYER4_LISTEN_MAX is too small"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
tcpfn_used++;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpfn[n].port = port;
|
||||||
|
tcpfn[n].func = func;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bx_bool vnet_server_c::unregister_tcp_handler(unsigned port)
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
|
||||||
|
for (n = 0; n < tcpfn_used; n++) {
|
||||||
|
if (tcpfn[n].port == port) {
|
||||||
|
tcpfn[n].func = (tcp_handler_t)NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BX_ERROR(("TCP port %u is not registered", port));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCP connection handling functions
|
||||||
|
|
||||||
|
tcp_conn_t *tcp_connections = NULL;
|
||||||
|
|
||||||
|
tcp_conn_t *tcp_new_connection(Bit8u clientid, Bit16u src_port, Bit16u dst_port)
|
||||||
|
{
|
||||||
|
tcp_conn_t *tc = new tcp_conn_t;
|
||||||
|
tc->clientid = clientid;
|
||||||
|
tc->src_port = src_port;
|
||||||
|
tc->dst_port = dst_port;
|
||||||
|
tc->state = TCP_DISCONNECTED;
|
||||||
|
tc->data = NULL;
|
||||||
|
tc->next = tcp_connections;
|
||||||
|
tcp_connections = tc;
|
||||||
|
return tc;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp_conn_t *tcp_find_connection(Bit8u clientid, Bit16u src_port, Bit16u dst_port)
|
||||||
|
{
|
||||||
|
tcp_conn_t *tc = tcp_connections;
|
||||||
|
while (tc != NULL) {
|
||||||
|
if ((tc->clientid != clientid) || (tc->src_port != src_port) || (tc->dst_port != dst_port))
|
||||||
|
tc = tc->next;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return tc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tcp_remove_connection(tcp_conn_t *tc)
|
||||||
|
{
|
||||||
|
tcp_conn_t *last;
|
||||||
|
|
||||||
|
if (tcp_connections == tc) {
|
||||||
|
tcp_connections = tc->next;
|
||||||
|
} else {
|
||||||
|
last = tcp_connections;
|
||||||
|
while (last != NULL) {
|
||||||
|
if (last->next != tc)
|
||||||
|
last = last->next;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (last) {
|
||||||
|
last->next = tc->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete tc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vnet_server_c::process_tcpipv4(Bit8u clientid, const Bit8u *ipheader,
|
||||||
|
unsigned ipheader_len, const Bit8u *l4pkt,
|
||||||
|
unsigned l4pkt_len)
|
||||||
|
{
|
||||||
|
unsigned tcphdr_len, tcpdata_len, tcprhdr_len, tcprdata_len = 0;
|
||||||
|
Bit32u tcp_seq_num, tcp_ack_num;
|
||||||
|
Bit16u tcp_src_port, tcp_dst_port, tcp_window;
|
||||||
|
Bit8u replybuf[MIN_RX_PACKET_LEN];
|
||||||
|
const Bit8u *tcp_data;
|
||||||
|
tcp_handler_t func;
|
||||||
|
tcp_conn_t *tcp_conn;
|
||||||
|
bx_bool tcp_error = 1;
|
||||||
|
|
||||||
|
if (l4pkt_len < 20) return;
|
||||||
tcp_header_t *tcphdr = (tcp_header_t *)l4pkt;
|
tcp_header_t *tcphdr = (tcp_header_t *)l4pkt;
|
||||||
tcp_header_t *tcprhdr = (tcp_header_t *)&reply[34];
|
tcp_header_t *tcprhdr = (tcp_header_t *)&replybuf[34];
|
||||||
tcp_src_port = ntohs(tcphdr->src_port);
|
tcp_src_port = ntohs(tcphdr->src_port);
|
||||||
tcp_dst_port = ntohs(tcphdr->dst_port);
|
tcp_dst_port = ntohs(tcphdr->dst_port);
|
||||||
guest_seq_num = ntohl(tcphdr->seq_num);
|
tcp_seq_num = ntohl(tcphdr->seq_num);
|
||||||
if (tcphdr->flags.syn) {
|
tcp_ack_num = ntohl(tcphdr->ack_num);
|
||||||
memset(tcprhdr, 0, sizeof(tcp_header_t));
|
tcphdr_len = tcphdr->data_offset << 2;
|
||||||
|
tcp_window = ntohs(tcphdr->window);
|
||||||
|
tcpdata_len = l4pkt_len - tcphdr_len;
|
||||||
|
tcp_data = (Bit8u*)tcphdr + tcphdr_len;
|
||||||
|
if (tcphdr_len > 20) {
|
||||||
|
BX_ERROR(("TCP options not supported yet"));
|
||||||
|
}
|
||||||
|
// check header
|
||||||
|
func = get_tcp_handler(tcp_dst_port);
|
||||||
|
tcp_conn = tcp_find_connection(clientid, tcp_src_port, tcp_dst_port);
|
||||||
|
tcprhdr_len = sizeof(tcp_header_t);
|
||||||
|
memset(tcprhdr, 0, tcprhdr_len);
|
||||||
|
if (func != (tcp_handler_t)NULL) {
|
||||||
|
if (tcp_conn == (tcp_conn_t*)NULL) {
|
||||||
|
if (tcphdr->flags.syn) {
|
||||||
|
tcprhdr->flags.syn = 1;
|
||||||
|
tcprhdr->flags.ack = 1;
|
||||||
|
tcprhdr->seq_num = htonl(1);
|
||||||
|
tcprhdr->ack_num = htonl(tcp_seq_num+1);
|
||||||
|
tcprhdr->window = htons(tcp_window);
|
||||||
|
tcprdata_len = 0;
|
||||||
|
tcp_conn = tcp_new_connection(clientid, tcp_src_port, tcp_dst_port);
|
||||||
|
tcp_conn->state = TCP_CONNECTING;
|
||||||
|
tcp_error = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((tcphdr->flags.fin) && (tcp_conn->state == TCP_CONNECTED)) {
|
||||||
|
tcprhdr->flags.fin = 1;
|
||||||
|
tcprhdr->flags.ack = 1;
|
||||||
|
tcprhdr->seq_num = htonl(tcp_conn->host_seq_num);
|
||||||
|
tcprhdr->ack_num = htonl(tcp_seq_num+1);
|
||||||
|
tcprhdr->window = htons(tcp_window);
|
||||||
|
tcprdata_len = 0;
|
||||||
|
tcp_conn->state = TCP_DISCONNECTING;
|
||||||
|
tcp_error = 0;
|
||||||
|
} else if (tcphdr->flags.ack) {
|
||||||
|
if (tcp_conn->state == TCP_CONNECTING) {
|
||||||
|
tcp_conn->state = TCP_CONNECTED;
|
||||||
|
tcp_conn->guest_seq_num = tcp_seq_num;
|
||||||
|
tcp_conn->host_seq_num = tcp_ack_num;
|
||||||
|
tcp_conn->window = tcp_window;
|
||||||
|
(*func)((void *)this, tcp_conn, tcp_data, tcpdata_len);
|
||||||
|
} else if (tcp_conn->state == TCP_DISCONNECTING) {
|
||||||
|
tcp_remove_connection(tcp_conn);
|
||||||
|
} else {
|
||||||
|
tcp_conn->guest_seq_num = tcp_seq_num;
|
||||||
|
tcp_conn->host_seq_num = tcp_ack_num;
|
||||||
|
if (tcpdata_len > 0) {
|
||||||
|
tcpipv4_send_ack(tcp_conn, tcpdata_len);
|
||||||
|
(*func)((void *)this, tcp_conn, tcp_data, tcpdata_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
BX_ERROR(("TCP: unhandled"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tcp_error) {
|
||||||
tcprhdr->flags.syn = 0;
|
tcprhdr->flags.syn = 0;
|
||||||
tcprhdr->flags.rst = 1;
|
tcprhdr->flags.rst = 1;
|
||||||
tcprhdr->flags.ack = 1;
|
tcprhdr->flags.ack = 1;
|
||||||
tcprhdr->seq_num = htonl(host_seq_num);
|
tcprhdr->seq_num = htonl(1);
|
||||||
tcprhdr->ack_num = htonl(guest_seq_num+1);
|
tcprhdr->ack_num = htonl(tcp_seq_num+1);
|
||||||
tcprhdr->window = 0;
|
tcprhdr->window = 0;
|
||||||
tcphdr_len = 20;
|
tcprdata_len = 0;
|
||||||
tcpdata_len = 0;
|
BX_ERROR(("tcp - port %u unhandled or in use", tcp_dst_port));
|
||||||
}
|
}
|
||||||
BX_ERROR(("tcp packet - not implemented (port = %d)", tcp_dst_port));
|
host_to_guest_tcpipv4(clientid, tcp_dst_port, tcp_src_port, replybuf,
|
||||||
// host-to-guest
|
tcprdata_len, tcprhdr_len);
|
||||||
if ((tcpdata_len + 54U) > BX_PACKET_BUFSIZE) {
|
|
||||||
BX_ERROR(("generated tcp data is too long"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// tcp pseudo-header
|
|
||||||
reply[34U-12U]=0;
|
|
||||||
reply[34U-11U]=0x06; // TCP
|
|
||||||
put_net2(&reply[34U-10U],tcphdr_len+tcpdata_len);
|
|
||||||
memcpy(&reply[34U-8U], dhcp->host_ipv4addr, 4);
|
|
||||||
memcpy(&reply[34U-4U], client[clientid].ipv4addr, 4);
|
|
||||||
// tcp header
|
|
||||||
tcprhdr->src_port = htons(tcp_dst_port);
|
|
||||||
tcprhdr->dst_port = htons(tcp_src_port);
|
|
||||||
tcprhdr->data_offset = tcphdr_len >> 2;
|
|
||||||
tcprhdr->checksum = 0;
|
|
||||||
put_net2(&reply[34U+16U], ip_checksum(&reply[34U-12U],12U+tcphdr_len+tcpdata_len) ^ (Bit16u)0xffff);
|
|
||||||
// ip header
|
|
||||||
memset(&reply[14U],0,20U);
|
|
||||||
reply[14U+0] = 0x45;
|
|
||||||
reply[14U+1] = 0x00;
|
|
||||||
put_net2(&reply[14U+2],20U+tcphdr_len+tcpdata_len);
|
|
||||||
put_net2(&reply[14U+4],1);
|
|
||||||
reply[14U+6] = 0x00;
|
|
||||||
reply[14U+7] = 0x00;
|
|
||||||
reply[14U+8] = 0x07; // TTL
|
|
||||||
reply[14U+9] = 0x06; // TCP
|
|
||||||
return (tcpdata_len+tcphdr_len+34);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vnet_server_c::host_to_guest_tcpipv4(Bit8u clientid, Bit16u src_port,
|
||||||
|
Bit16u dst_port, Bit8u *data,
|
||||||
|
unsigned data_len, unsigned hdr_len)
|
||||||
|
{
|
||||||
|
tcp_header_t *tcphdr = (tcp_header_t *)&data[34];
|
||||||
|
|
||||||
|
// tcp pseudo-header
|
||||||
|
data[34U-12U]=0;
|
||||||
|
data[34U-11U]=0x06; // TCP
|
||||||
|
put_net2(&data[34U-10U], hdr_len+data_len);
|
||||||
|
memcpy(&data[34U-8U], dhcp->host_ipv4addr, 4);
|
||||||
|
memcpy(&data[34U-4U], client[clientid].ipv4addr, 4);
|
||||||
|
// tcp header
|
||||||
|
tcphdr->src_port = htons(src_port);
|
||||||
|
tcphdr->dst_port = htons(dst_port);
|
||||||
|
tcphdr->data_offset = hdr_len >> 2;
|
||||||
|
tcphdr->checksum = 0;
|
||||||
|
put_net2(&data[34U+16U], ip_checksum(&data[34U-12U],12U+hdr_len+data_len) ^ (Bit16u)0xffff);
|
||||||
|
// ip header
|
||||||
|
memset(&data[14U],0,20U);
|
||||||
|
data[14U+0] = 0x45;
|
||||||
|
data[14U+1] = 0x00;
|
||||||
|
put_net2(&data[14U+2],20U+hdr_len+data_len);
|
||||||
|
put_net2(&data[14U+4],1);
|
||||||
|
data[14U+6] = 0x00;
|
||||||
|
data[14U+7] = 0x00;
|
||||||
|
data[14U+8] = 0x07; // TTL
|
||||||
|
data[14U+9] = 0x06; // TCP
|
||||||
|
|
||||||
|
host_to_guest_ipv4(clientid, 0, data, data_len + hdr_len + 34U);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vnet_server_c::tcpipv4_send_data(const tcp_conn_t *tcp_conn, const Bit8u *data, unsigned data_len, bx_bool push)
|
||||||
|
{
|
||||||
|
Bit8u buffer[BX_PACKET_BUFSIZE];
|
||||||
|
tcp_header_t *tcphdr = (tcp_header_t *)&buffer[34];
|
||||||
|
unsigned tcphdr_len = sizeof(tcp_header_t);
|
||||||
|
Bit8u *tcp_data;
|
||||||
|
|
||||||
|
memset(tcphdr, 0, tcphdr_len);
|
||||||
|
if (push) {
|
||||||
|
tcphdr->flags.psh = 1;
|
||||||
|
}
|
||||||
|
tcphdr->flags.ack = 1;
|
||||||
|
tcphdr->seq_num = htonl(tcp_conn->host_seq_num);
|
||||||
|
tcphdr->ack_num = htonl(tcp_conn->guest_seq_num);
|
||||||
|
tcphdr->window = htons(tcp_conn->window);
|
||||||
|
tcp_data = (Bit8u*)tcphdr + tcphdr_len;
|
||||||
|
if ((data_len + 54U) > BX_PACKET_BUFSIZE) {
|
||||||
|
BX_ERROR(("generated tcp data is too long"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(tcp_data, data, data_len);
|
||||||
|
host_to_guest_tcpipv4(tcp_conn->clientid, tcp_conn->dst_port, tcp_conn->src_port,
|
||||||
|
buffer, data_len, tcphdr_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vnet_server_c::tcpipv4_send_ack(const tcp_conn_t *tcp_conn, unsigned data_len)
|
||||||
|
{
|
||||||
|
Bit8u replybuf[MIN_RX_PACKET_LEN];
|
||||||
|
tcp_header_t *tcphdr = (tcp_header_t *)&replybuf[34];
|
||||||
|
unsigned tcphdr_len = sizeof(tcp_header_t);
|
||||||
|
|
||||||
|
memset(replybuf, 0, MIN_RX_PACKET_LEN);
|
||||||
|
tcphdr->flags.ack = 1;
|
||||||
|
tcphdr->seq_num = htonl(tcp_conn->host_seq_num);
|
||||||
|
tcphdr->ack_num = htonl(tcp_conn->guest_seq_num + data_len);
|
||||||
|
tcphdr->window = htons(tcp_conn->window);
|
||||||
|
host_to_guest_tcpipv4(tcp_conn->clientid, tcp_conn->dst_port, tcp_conn->src_port,
|
||||||
|
replybuf, 0, tcphdr_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vnet_server_c::tcpipv4_ftp_handler(void *this_ptr, tcp_conn_t *tcp_conn, const Bit8u *data, unsigned data_len)
|
||||||
|
{
|
||||||
|
((vnet_server_c *)this_ptr)->tcpipv4_ftp_handler_ns(tcp_conn, data, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vnet_server_c::tcpipv4_ftp_handler_ns(tcp_conn_t *tcp_conn, const Bit8u *data, unsigned data_len)
|
||||||
|
{
|
||||||
|
char *ftpcmd, *cmd = NULL, *arg = NULL;
|
||||||
|
char reply[80];
|
||||||
|
ftp_session_t *fs;
|
||||||
|
|
||||||
|
reply[0] = 0;
|
||||||
|
if (tcp_conn->data == NULL) {
|
||||||
|
// send greeting message
|
||||||
|
sprintf(reply, "220 Bochs FTP server ready.%c%c", 13, 10);
|
||||||
|
fs = new ftp_session_t;
|
||||||
|
fs->state = 1;
|
||||||
|
tcp_conn->data = fs;
|
||||||
|
} else if (data_len > 0) {
|
||||||
|
ftpcmd = new char[data_len + 1];
|
||||||
|
memcpy(ftpcmd, data, data_len);
|
||||||
|
ftpcmd[data_len] = 0;
|
||||||
|
cmd = strtok(ftpcmd, " \r");
|
||||||
|
arg = strtok(NULL, "\r");
|
||||||
|
fs = (ftp_session_t*)tcp_conn->data;
|
||||||
|
if (fs->state == 1) {
|
||||||
|
if (!strcasecmp(cmd, "USER")) {
|
||||||
|
fs->anonymous = !strcmp(arg, "anonymous");
|
||||||
|
if (!strcmp(arg, "bochs") || fs->anonymous) {
|
||||||
|
sprintf(reply, "331 Password required for %s.%c%c", arg, 13, 10);
|
||||||
|
fs->state = 2;
|
||||||
|
} else {
|
||||||
|
sprintf(reply, "430 Invalid username or password.%c%c", 13, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (fs->state == 2) {
|
||||||
|
if (!strcasecmp(cmd, "PASS")) {
|
||||||
|
if (!strcmp(arg, "bochs") || fs->anonymous) {
|
||||||
|
sprintf(reply, "230 User %s logged in.%c%c", fs->anonymous ? "anonymous":"bochs", 13, 10);
|
||||||
|
fs->state = 3;
|
||||||
|
} else {
|
||||||
|
sprintf(reply, "530 Login incorrect.%c%c", 13, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!strcasecmp(cmd, "SYST")) {
|
||||||
|
sprintf(reply, "215 UNIX Type: Bochs Version: 2.6.11%c%c", 13, 10);
|
||||||
|
} else if (!strcasecmp(cmd, "FEAT")) {
|
||||||
|
sprintf(reply, "211 end%c%c", 13, 10);
|
||||||
|
} else if (!strcasecmp(cmd, "NOOP")) {
|
||||||
|
sprintf(reply, "200 OK.%c%c", 13, 10);
|
||||||
|
} else if (!strcasecmp(cmd, "OPTS")) {
|
||||||
|
sprintf(reply, "501 Feature '%s' not supported.%c%c", arg, 13, 10);
|
||||||
|
} else if (!strcasecmp(cmd, "PWD")) {
|
||||||
|
sprintf(reply, "257 \"/\" is current directory.%c%c", 13, 10);
|
||||||
|
} else if (!strcasecmp(cmd, "QUIT")) {
|
||||||
|
sprintf(reply, "221 Goodbye.%c%c", 13, 10);
|
||||||
|
delete fs;
|
||||||
|
tcp_conn->data = NULL;
|
||||||
|
} else {
|
||||||
|
sprintf(reply, "502 Command '%s' not implemented.%c%c", cmd, 13, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete [] ftpcmd;
|
||||||
|
}
|
||||||
|
if (strlen(reply) > 0) {
|
||||||
|
tcpipv4_send_data(tcp_conn, (Bit8u*)reply, strlen(reply), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layer 4 handler methods
|
||||||
|
|
||||||
layer4_handler_t vnet_server_c::get_layer4_handler(
|
layer4_handler_t vnet_server_c::get_layer4_handler(
|
||||||
unsigned ipprotocol, unsigned port)
|
unsigned ipprotocol, unsigned port)
|
||||||
{
|
{
|
||||||
@ -587,25 +934,30 @@ bx_bool vnet_server_c::unregister_layer4_handler(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int vnet_server_c::process_udpipv4(Bit8u clientid, const Bit8u *ipheader,
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
// UDP/IPv4
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void vnet_server_c::process_udpipv4(Bit8u clientid, const Bit8u *ipheader,
|
||||||
unsigned ipheader_len, const Bit8u *l4pkt,
|
unsigned ipheader_len, const Bit8u *l4pkt,
|
||||||
unsigned l4pkt_len, Bit8u *reply)
|
unsigned l4pkt_len)
|
||||||
{
|
{
|
||||||
unsigned udp_dst_port;
|
unsigned udp_dst_port;
|
||||||
unsigned udp_src_port;
|
unsigned udp_src_port;
|
||||||
int udp_len = 0;
|
int udp_len = 0;
|
||||||
Bit8u *udpreply = &reply[42];
|
Bit8u replybuf[BX_PACKET_BUFSIZE];
|
||||||
|
Bit8u *udpreply = &replybuf[42];
|
||||||
layer4_handler_t func;
|
layer4_handler_t func;
|
||||||
|
|
||||||
if (l4pkt_len < 8) return 0;
|
if (l4pkt_len < 8) return;
|
||||||
udp_header_t *udphdr = (udp_header_t *)l4pkt;
|
udp_header_t *udphdr = (udp_header_t *)l4pkt;
|
||||||
udp_src_port = ntohs(udphdr->src_port);
|
udp_src_port = ntohs(udphdr->src_port);
|
||||||
udp_dst_port = ntohs(udphdr->dst_port);
|
udp_dst_port = ntohs(udphdr->dst_port);
|
||||||
// udp_len = ntohs(udphdr->length);
|
// udp_len = ntohs(udphdr->length);
|
||||||
ip_header_t *iphdr = (ip_header_t *)ipheader;
|
ip_header_t *iphdr = (ip_header_t *)ipheader;
|
||||||
bx_bool dns_service = !memcmp(&iphdr->dst_addr, dhcp->dns_ipv4addr, 4);
|
bx_bool dns_srv = !memcmp(&iphdr->dst_addr, dhcp->dns_ipv4addr, 4);
|
||||||
if (dns_service != (udp_dst_port ==INET_PORT_DOMAIN))
|
if (dns_srv != (udp_dst_port ==INET_PORT_DOMAIN))
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
func = get_layer4_handler(0x11, udp_dst_port);
|
func = get_layer4_handler(0x11, udp_dst_port);
|
||||||
if (func != (layer4_handler_t)NULL) {
|
if (func != (layer4_handler_t)NULL) {
|
||||||
@ -617,36 +969,37 @@ int vnet_server_c::process_udpipv4(Bit8u clientid, const Bit8u *ipheader,
|
|||||||
if (udp_len > 0) {
|
if (udp_len > 0) {
|
||||||
if ((udp_len + 42U) > BX_PACKET_BUFSIZE) {
|
if ((udp_len + 42U) > BX_PACKET_BUFSIZE) {
|
||||||
BX_ERROR(("generated udp data is too long"));
|
BX_ERROR(("generated udp data is too long"));
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
// udp pseudo-header
|
// udp pseudo-header
|
||||||
reply[34U-12U] = 0;
|
replybuf[34U-12U] = 0;
|
||||||
reply[34U-11U] = 0x11; // UDP
|
replybuf[34U-11U] = 0x11; // UDP
|
||||||
put_net2(&reply[34U-10U], 8U+udp_len);
|
put_net2(&replybuf[34U-10U], 8U+udp_len);
|
||||||
if (dns_service) {
|
if (dns_srv) {
|
||||||
memcpy(&reply[34U-8U], dhcp->dns_ipv4addr, 4);
|
memcpy(&replybuf[34U-8U], dhcp->dns_ipv4addr, 4);
|
||||||
} else {
|
} else {
|
||||||
memcpy(&reply[34U-8U], dhcp->host_ipv4addr, 4);
|
memcpy(&replybuf[34U-8U], dhcp->host_ipv4addr, 4);
|
||||||
}
|
}
|
||||||
memcpy(&reply[34U-4U], client[clientid].ipv4addr, 4);
|
memcpy(&replybuf[34U-4U], client[clientid].ipv4addr, 4);
|
||||||
// udp header
|
// udp header
|
||||||
put_net2(&reply[34U+0], udp_dst_port);
|
put_net2(&replybuf[34U+0], udp_dst_port);
|
||||||
put_net2(&reply[34U+2], udp_src_port);
|
put_net2(&replybuf[34U+2], udp_src_port);
|
||||||
put_net2(&reply[34U+4],8U+udp_len);
|
put_net2(&replybuf[34U+4],8U+udp_len);
|
||||||
put_net2(&reply[34U+6],0);
|
put_net2(&replybuf[34U+6],0);
|
||||||
put_net2(&reply[34U+6], ip_checksum(&reply[34U-12U],12U+8U+udp_len) ^ (Bit16u)0xffff);
|
put_net2(&replybuf[34U+6], ip_checksum(&replybuf[34U-12U],12U+8U+udp_len) ^ (Bit16u)0xffff);
|
||||||
// ip header
|
// ip header
|
||||||
memset(&reply[14U], 0, 20U);
|
memset(&replybuf[14U], 0, 20U);
|
||||||
reply[14U+0] = 0x45;
|
replybuf[14U+0] = 0x45;
|
||||||
reply[14U+1] = 0x00;
|
replybuf[14U+1] = 0x00;
|
||||||
put_net2(&reply[14U+2], 20U+8U+udp_len);
|
put_net2(&replybuf[14U+2], 20U+8U+udp_len);
|
||||||
put_net2(&reply[14U+4], 1);
|
put_net2(&replybuf[14U+4], 1);
|
||||||
reply[14U+6] = 0x00;
|
replybuf[14U+6] = 0x00;
|
||||||
reply[14U+7] = 0x00;
|
replybuf[14U+7] = 0x00;
|
||||||
reply[14U+8] = 0x07; // TTL
|
replybuf[14U+8] = 0x07; // TTL
|
||||||
reply[14U+9] = 0x11; // UDP
|
replybuf[14U+9] = 0x11; // UDP
|
||||||
|
|
||||||
|
host_to_guest_ipv4(clientid, dns_srv, replybuf, udp_len + 42U);
|
||||||
}
|
}
|
||||||
return (udp_len + 42);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int vnet_server_c::udpipv4_dhcp_handler(void *this_ptr, const Bit8u *ipheader,
|
int vnet_server_c::udpipv4_dhcp_handler(void *this_ptr, const Bit8u *ipheader,
|
||||||
|
@ -196,6 +196,32 @@ typedef int (*layer4_handler_t)(
|
|||||||
Bit8u *reply
|
Bit8u *reply
|
||||||
);
|
);
|
||||||
|
|
||||||
|
typedef struct tcp_conn {
|
||||||
|
Bit8u clientid;
|
||||||
|
Bit16u src_port;
|
||||||
|
Bit16u dst_port;
|
||||||
|
Bit32u host_seq_num;
|
||||||
|
Bit32u guest_seq_num;
|
||||||
|
Bit16u window;
|
||||||
|
Bit8u state;
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef struct ftp_session {
|
||||||
|
Bit8u state;
|
||||||
|
bx_bool anonymous;
|
||||||
|
struct ftp_session *next;
|
||||||
|
} ftp_session_t;
|
||||||
|
|
||||||
|
|
||||||
class vnet_server_c {
|
class vnet_server_c {
|
||||||
public:
|
public:
|
||||||
vnet_server_c();
|
vnet_server_c();
|
||||||
@ -203,7 +229,8 @@ public:
|
|||||||
|
|
||||||
void init(bx_devmodel_c *netdev, dhcp_cfg_t *dhcpc, const char *tftp_rootdir);
|
void init(bx_devmodel_c *netdev, dhcp_cfg_t *dhcpc, const char *tftp_rootdir);
|
||||||
void init_client(Bit8u clientid, const Bit8u *macaddr, const Bit8u *default_ipv4addr);
|
void init_client(Bit8u clientid, const Bit8u *macaddr, const Bit8u *default_ipv4addr);
|
||||||
int handle_packet(const Bit8u *buf, unsigned len, Bit8u *reply);
|
void handle_packet(const Bit8u *buf, unsigned len);
|
||||||
|
unsigned get_packet(Bit8u *buf);
|
||||||
#ifdef BXHUB
|
#ifdef BXHUB
|
||||||
void init_log(const char *logfn);
|
void init_log(const char *logfn);
|
||||||
#endif
|
#endif
|
||||||
@ -213,21 +240,41 @@ public:
|
|||||||
layer4_handler_t func);
|
layer4_handler_t func);
|
||||||
bx_bool unregister_layer4_handler(unsigned ipprotocol, unsigned port);
|
bx_bool unregister_layer4_handler(unsigned ipprotocol, unsigned port);
|
||||||
|
|
||||||
|
tcp_handler_t get_tcp_handler(unsigned port);
|
||||||
|
bx_bool register_tcp_handler(unsigned port, tcp_handler_t func);
|
||||||
|
bx_bool unregister_tcp_handler(unsigned port);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifdef BXHUB
|
#ifdef BXHUB
|
||||||
void bx_printf(const char *fmt, ...);
|
void bx_printf(const char *fmt, ...);
|
||||||
#endif
|
#endif
|
||||||
bx_bool find_client(const Bit8u *mac_addr, Bit8u *clientid);
|
bx_bool find_client(const Bit8u *mac_addr, Bit8u *clientid);
|
||||||
|
bx_bool find_client2(const Bit8u *ipv4addr, Bit8u *clientid);
|
||||||
|
|
||||||
int process_arp(Bit8u clientid, const Bit8u *buf, unsigned len, Bit8u *reply);
|
void host_to_guest(Bit8u clientid, Bit8u *buf, unsigned len, unsigned l3type);
|
||||||
int process_ipv4(Bit8u clientid, const Bit8u *buf, unsigned len, Bit8u *reply);
|
|
||||||
|
|
||||||
int process_icmpipv4(Bit8u clientid, const Bit8u *ipheader, unsigned ipheader_len,
|
void process_arp(Bit8u clientid, const Bit8u *buf, unsigned len);
|
||||||
const Bit8u *l4pkt, unsigned l4pkt_len, Bit8u *reply);
|
|
||||||
int process_tcpipv4(Bit8u clientid, const Bit8u *ipheader, unsigned ipheader_len,
|
void process_ipv4(Bit8u clientid, const Bit8u *buf, unsigned len);
|
||||||
const Bit8u *l4pkt, unsigned l4pkt_len, Bit8u *reply);
|
void host_to_guest_ipv4(Bit8u clientid, bx_bool dns_srv, Bit8u *buf, unsigned len);
|
||||||
int process_udpipv4(Bit8u clientid, const Bit8u *ipheader, unsigned ipheader_len,
|
|
||||||
const Bit8u *l4pkt, unsigned l4pkt_len, Bit8u *reply);
|
void process_icmpipv4(Bit8u clientid, const Bit8u *ipheader, unsigned ipheader_len,
|
||||||
|
const Bit8u *l4pkt, unsigned l4pkt_len);
|
||||||
|
void process_tcpipv4(Bit8u clientid, const Bit8u *ipheader, unsigned ipheader_len,
|
||||||
|
const Bit8u *l4pkt, unsigned l4pkt_len);
|
||||||
|
void process_udpipv4(Bit8u clientid, const Bit8u *ipheader, unsigned ipheader_len,
|
||||||
|
const Bit8u *l4pkt, unsigned l4pkt_len);
|
||||||
|
|
||||||
|
void host_to_guest_tcpipv4(Bit8u clientid, Bit16u src_port, Bit16u dst_port,
|
||||||
|
Bit8u *data, unsigned data_len, unsigned hdr_len);
|
||||||
|
void tcpipv4_send_data(const tcp_conn_t *tcp_conn, const Bit8u *data,
|
||||||
|
unsigned data_len, bx_bool push);
|
||||||
|
void tcpipv4_send_ack(const tcp_conn_t *tcp_conn, unsigned data_len);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
static int udpipv4_dhcp_handler(void *this_ptr, const Bit8u *ipheader,
|
static int udpipv4_dhcp_handler(void *this_ptr, const Bit8u *ipheader,
|
||||||
unsigned ipheader_len, unsigned sourceport,
|
unsigned ipheader_len, unsigned sourceport,
|
||||||
@ -275,7 +322,17 @@ private:
|
|||||||
layer4_handler_t func;
|
layer4_handler_t func;
|
||||||
} l4data[LAYER4_LISTEN_MAX];
|
} l4data[LAYER4_LISTEN_MAX];
|
||||||
|
|
||||||
|
struct {
|
||||||
|
unsigned port;
|
||||||
|
tcp_handler_t func;
|
||||||
|
} tcpfn[LAYER4_LISTEN_MAX];
|
||||||
|
|
||||||
unsigned l4data_used;
|
unsigned l4data_used;
|
||||||
|
unsigned tcpfn_used;
|
||||||
|
|
||||||
|
Bit8u *packet_buffer[2];
|
||||||
|
unsigned packet_len[2];
|
||||||
|
Bit8u packet_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -80,7 +80,7 @@ typedef struct {
|
|||||||
Bit8u macaddr[6];
|
Bit8u macaddr[6];
|
||||||
Bit8u default_ipv4addr[4];
|
Bit8u default_ipv4addr[4];
|
||||||
Bit8u *reply_buffer;
|
Bit8u *reply_buffer;
|
||||||
int pending_reply_size;
|
unsigned pending_reply_size;
|
||||||
} hub_client_t;
|
} hub_client_t;
|
||||||
|
|
||||||
const Bit8u default_host_macaddr[6] = {0xb0, 0xc4, 0x20, 0x00, 0x00, 0x0f};
|
const Bit8u default_host_macaddr[6] = {0xb0, 0xc4, 0x20, 0x00, 0x00, 0x0f};
|
||||||
@ -120,10 +120,8 @@ bx_bool handle_packet(hub_client_t *client, Bit8u *buf, unsigned len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client->pending_reply_size > 0)
|
vnet_server.handle_packet(buf, len);
|
||||||
return 0;
|
client->pending_reply_size = vnet_server.get_packet(client->reply_buffer);
|
||||||
|
|
||||||
client->pending_reply_size = vnet_server.handle_packet(buf, len, client->reply_buffer);
|
|
||||||
return (client->pending_reply_size > 0);
|
return (client->pending_reply_size > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,7 +364,8 @@ int CDECL main(int argc, char **argv)
|
|||||||
// send reply from builtin service
|
// send reply from builtin service
|
||||||
if (hclient[i].pending_reply_size > 0) {
|
if (hclient[i].pending_reply_size > 0) {
|
||||||
send_packet(&hclient[i], hclient[i].reply_buffer, hclient[i].pending_reply_size);
|
send_packet(&hclient[i], hclient[i].reply_buffer, hclient[i].pending_reply_size);
|
||||||
hclient[i].pending_reply_size = 0;
|
// check for another pending packet
|
||||||
|
hclient[i].pending_reply_size = vnet_server.get_packet(hclient[i].reply_buffer);
|
||||||
}
|
}
|
||||||
// check MAC address of new client
|
// check MAC address of new client
|
||||||
if (hclient[i].init != 0) {
|
if (hclient[i].init != 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user