178 lines
4.7 KiB
C++
178 lines
4.7 KiB
C++
#ifndef __SSHPORT_H
|
|
#define __SSHPORT_H
|
|
|
|
#include <queue>
|
|
|
|
#include <linux/if.h>
|
|
#include <linux/if_tun.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#define NET_IP_ALIGN 2
|
|
#define ETH_MAX_WORDS 190
|
|
#define ETH_MAX_BYTES 1518
|
|
|
|
struct network_flit {
|
|
uint64_t data;
|
|
bool last;
|
|
};
|
|
|
|
/* The other side of this port is a TAP interface to the host network.
|
|
* This allows users to ssh into a simulated cluster */
|
|
class SSHPort : public BasePort {
|
|
public:
|
|
SSHPort(int portNo);
|
|
void tick();
|
|
void tick_pre();
|
|
void send();
|
|
void recv();
|
|
|
|
private:
|
|
int sshtapfd;
|
|
uint64_t tap_send_buffer[ETH_MAX_WORDS], tap_recv_buffer[ETH_MAX_WORDS];
|
|
void *tap_send_frame = ((char *)tap_send_buffer) + NET_IP_ALIGN;
|
|
void *tap_recv_frame = ((char *)tap_recv_buffer) + NET_IP_ALIGN;
|
|
int tap_send_idx = 0, tap_len;
|
|
bool tap_can_send = false;
|
|
std::queue<network_flit> out_flits;
|
|
std::queue<network_flit> in_flits;
|
|
};
|
|
|
|
/* open TAP device */
|
|
static int tuntap_alloc(const char *dev, int flags) {
|
|
struct ifreq ifr;
|
|
int tapfd, err;
|
|
|
|
if ((tapfd = open("/dev/net/tun", O_RDWR | O_NONBLOCK)) < 0) {
|
|
perror("open()");
|
|
return tapfd;
|
|
}
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
ifr.ifr_flags = flags;
|
|
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
|
|
|
|
if ((err = ioctl(tapfd, TUNSETIFF, &ifr)) < 0) {
|
|
perror("ioctl()");
|
|
close(tapfd);
|
|
return err;
|
|
}
|
|
|
|
return tapfd;
|
|
}
|
|
|
|
#define DEVNAME_BYTES 128
|
|
#define ceil_div(n, d) (((n)-1) / (d) + 1)
|
|
|
|
SSHPort::SSHPort(int portNo) : BasePort(portNo, false) {
|
|
char *slotid =
|
|
NULL; // placeholder for multiple SSH port support if we need it later
|
|
char devname[DEVNAME_BYTES + 1];
|
|
devname[0] = '\0';
|
|
strncat(devname, "tap", DEVNAME_BYTES);
|
|
|
|
if (!slotid) {
|
|
fprintf(stderr, "Slot ID not specified. Assuming tap0\n");
|
|
slotid = (char *)"0";
|
|
}
|
|
strncat(devname, slotid, DEVNAME_BYTES - 3);
|
|
|
|
sshtapfd = tuntap_alloc(devname, IFF_TAP | IFF_NO_PI);
|
|
if (sshtapfd < 0) {
|
|
fprintf(stderr, "Could not open tap interface %s\n", devname);
|
|
abort();
|
|
}
|
|
|
|
current_input_buf = (uint8_t *)calloc(sizeof(uint8_t), BUFSIZE_BYTES);
|
|
current_output_buf = (uint8_t *)calloc(sizeof(uint8_t), BUFSIZE_BYTES);
|
|
}
|
|
|
|
void SSHPort::send() {
|
|
// here, we take data that was written to the port by the switch
|
|
// (data is in current_output_buf)
|
|
// and push it into queues to send into the TAP
|
|
|
|
if (((uint64_t *)current_output_buf)[0] == 0xDEADBEEFDEADBEEFL) {
|
|
// if compress flag is set, clear it, this port type doesn't care
|
|
// (and in fact, we're writing too much, so stuff later will get confused)
|
|
((uint64_t *)current_output_buf)[0] = 0L;
|
|
}
|
|
|
|
// first, push into out_flits queue
|
|
for (int tokenno = 0; tokenno < NUM_TOKENS; tokenno++) {
|
|
if (is_valid_flit(current_output_buf, tokenno)) {
|
|
struct network_flit flt;
|
|
flt.data = get_flit(current_output_buf, tokenno);
|
|
flt.last = is_last_flit(current_output_buf, tokenno);
|
|
out_flits.push(flt);
|
|
}
|
|
}
|
|
|
|
// then, actually send stuff out on the TAP
|
|
// next, see if there is data to send
|
|
if (!tap_can_send) {
|
|
while (!out_flits.empty()) {
|
|
tap_send_buffer[tap_send_idx] = out_flits.front().data;
|
|
tap_can_send = out_flits.front().last;
|
|
out_flits.pop();
|
|
tap_send_idx++;
|
|
if (tap_can_send)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tap_can_send) {
|
|
tap_len = tap_send_idx * sizeof(uint64_t) - NET_IP_ALIGN;
|
|
if (::write(sshtapfd, tap_send_frame, tap_len) >= 0) {
|
|
tap_send_idx = 0;
|
|
tap_can_send = false;
|
|
} else if (errno != EAGAIN) {
|
|
perror("send()");
|
|
abort();
|
|
}
|
|
}
|
|
|
|
// finally, clear current_output_buf for the next iter
|
|
memset(current_output_buf, 0x0, BUFSIZE_BYTES);
|
|
}
|
|
|
|
void SSHPort::recv() {
|
|
// clear the input buf leftover from previous cycle
|
|
memset(current_input_buf, 0x0, BUFSIZE_BYTES);
|
|
|
|
// pull in flits from the TAP
|
|
tap_len = ::read(sshtapfd, tap_recv_frame, ETH_MAX_BYTES);
|
|
if (tap_len >= 0) {
|
|
int i, n = ceil_div(tap_len + NET_IP_ALIGN, sizeof(uint64_t));
|
|
for (i = 0; i < n; i++) {
|
|
struct network_flit flt;
|
|
flt.data = tap_recv_buffer[i];
|
|
flt.last = i == (n - 1);
|
|
in_flits.push(flt);
|
|
}
|
|
} else if (errno != EAGAIN) {
|
|
perror("recv()");
|
|
abort();
|
|
}
|
|
|
|
// next, pull off of in_flits until current_input_buf is full, or we have
|
|
// nothing left to write
|
|
|
|
for (int tokenno = 0; tokenno < NUM_TOKENS; tokenno++) {
|
|
if (!in_flits.empty()) {
|
|
write_last_flit(current_input_buf, tokenno, in_flits.front().last);
|
|
write_valid_flit(current_input_buf, tokenno);
|
|
write_flit(current_input_buf, tokenno, in_flits.front().data);
|
|
in_flits.pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SSHPort::tick() {
|
|
// don't need to do anything for SSHPorts
|
|
}
|
|
|
|
void SSHPort::tick_pre() {
|
|
// don't need to do anything for SSHPorts
|
|
}
|
|
#endif // __SSHPORT_H
|