377 lines
12 KiB
C
377 lines
12 KiB
C
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <ntlite/syscall.h>
|
|
|
|
#define PING_DEFAULT_TARGET "10.0.2.2"
|
|
#define PING_DEFAULT_COUNT 4u
|
|
#define PING_TIMEOUT_MS 1000u
|
|
#define US_PER_MS 1000ull
|
|
#define PING_PAYLOAD_BYTES 32u
|
|
#define PING_PACKET_BYTES (8u + PING_PAYLOAD_BYTES)
|
|
|
|
static void print(const char* s) {
|
|
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
|
}
|
|
|
|
static void print_char(char c) {
|
|
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
|
SysWriteConsole(&req);
|
|
}
|
|
|
|
static void print_u64(uint64_t v) {
|
|
char buf[32];
|
|
int n = 0;
|
|
if (v == 0) { print("0"); return; }
|
|
while (v && n < (int)sizeof(buf)) {
|
|
buf[n++] = (char)('0' + (v % 10));
|
|
v /= 10;
|
|
}
|
|
while (n > 0) print_char(buf[--n]);
|
|
}
|
|
|
|
static void print_ipv4(const uint8_t ip[4]) {
|
|
print_u64(ip[0]); print("."); print_u64(ip[1]); print(".");
|
|
print_u64(ip[2]); print("."); print_u64(ip[3]);
|
|
}
|
|
|
|
static void print_rtt_us(uint64_t us) {
|
|
uint64_t whole = us / US_PER_MS;
|
|
uint64_t frac = us % US_PER_MS;
|
|
print_u64(whole);
|
|
print(".");
|
|
if (frac < 100) print("0");
|
|
if (frac < 10) print("0");
|
|
print_u64(frac);
|
|
}
|
|
|
|
static void format_ipv4(char* out, size_t outSize, const uint8_t ip[4]) {
|
|
if (!out || outSize == 0) return;
|
|
size_t off = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
char tmp[4];
|
|
uint32_t v = ip[i];
|
|
int n = 0;
|
|
if (v >= 100) tmp[n++] = (char)('0' + (v / 100));
|
|
if (v >= 10) tmp[n++] = (char)('0' + ((v / 10) % 10));
|
|
tmp[n++] = (char)('0' + (v % 10));
|
|
for (int j = 0; j < n && off + 1 < outSize; j++) out[off++] = tmp[j];
|
|
if (i != 3 && off + 1 < outSize) out[off++] = '.';
|
|
}
|
|
out[off < outSize ? off : outSize - 1] = 0;
|
|
}
|
|
|
|
static const char* next_token(const char* s, char* out, size_t outSize) {
|
|
if (!s || !out || outSize == 0) return NULL;
|
|
while (*s == ' ' || *s == '\t') s++;
|
|
if (*s == 0) { out[0] = 0; return NULL; }
|
|
|
|
size_t n = 0;
|
|
while (*s && *s != ' ' && *s != '\t') {
|
|
if (n + 1 < outSize) out[n++] = *s;
|
|
s++;
|
|
}
|
|
out[n] = 0;
|
|
return s;
|
|
}
|
|
|
|
static bool parse_u8(const char** p, uint8_t* out) {
|
|
const char* s = *p;
|
|
uint32_t v = 0;
|
|
int digits = 0;
|
|
while (*s >= '0' && *s <= '9') {
|
|
v = v * 10u + (uint32_t)(*s - '0');
|
|
if (v > 255u) return false;
|
|
s++;
|
|
digits++;
|
|
}
|
|
if (digits == 0) return false;
|
|
*out = (uint8_t)v;
|
|
*p = s;
|
|
return true;
|
|
}
|
|
|
|
static bool parse_ipv4(const char* s, uint8_t ip[4]) {
|
|
if (!s || !ip) return false;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (!parse_u8(&s, &ip[i])) return false;
|
|
if (i != 3) {
|
|
if (*s != '.') return false;
|
|
s++;
|
|
}
|
|
}
|
|
return *s == 0;
|
|
}
|
|
|
|
static bool parse_u32(const char* s, uint32_t* out) {
|
|
if (!s || !*s || !out) return false;
|
|
uint32_t v = 0;
|
|
while (*s >= '0' && *s <= '9') {
|
|
uint32_t d = (uint32_t)(*s - '0');
|
|
if (v > (0xFFFFFFFFu - d) / 10u) return false;
|
|
v = v * 10u + d;
|
|
s++;
|
|
}
|
|
if (*s != 0) return false;
|
|
*out = v;
|
|
return true;
|
|
}
|
|
|
|
static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
|
|
if (parse_ipv4(host, ip))
|
|
return SYS_STATUS_SUCCESS;
|
|
|
|
SYS_SOCKET_ADDRESS_IPV4 resolved;
|
|
size_t resolvedSize = sizeof(resolved);
|
|
SYS_RESOLVE_HOST_REQUEST req;
|
|
memset(&resolved, 0, sizeof(resolved));
|
|
memset(&req, 0, sizeof(req));
|
|
req.Name = SysSvFromCstr(host);
|
|
req.Family = SYS_AF_INET;
|
|
req.AddressOut = &resolved;
|
|
req.AddressSizeInOut = &resolvedSize;
|
|
|
|
uint32_t st = SysResolveHost(&req);
|
|
if (st != SYS_STATUS_SUCCESS)
|
|
return st;
|
|
if (resolved.Family != SYS_AF_INET)
|
|
return SYS_STATUS_NOT_FOUND;
|
|
|
|
ip[0] = resolved.Address[0];
|
|
ip[1] = resolved.Address[1];
|
|
ip[2] = resolved.Address[2];
|
|
ip[3] = resolved.Address[3];
|
|
return SYS_STATUS_SUCCESS;
|
|
}
|
|
|
|
static uint16_t icmp_checksum(const uint8_t* data, size_t length) {
|
|
uint32_t sum = 0;
|
|
while (length >= 2) {
|
|
sum += ((uint32_t)data[0] << 8) | data[1];
|
|
data += 2;
|
|
length -= 2;
|
|
}
|
|
if (length)
|
|
sum += ((uint32_t)data[0] << 8);
|
|
|
|
while (sum >> 16)
|
|
sum = (sum & 0xFFFFu) + (sum >> 16);
|
|
return (uint16_t)(~sum & 0xFFFFu);
|
|
}
|
|
|
|
static void make_echo_request(uint8_t packet[PING_PACKET_BYTES], uint16_t ident, uint16_t seq) {
|
|
memset(packet, 0, PING_PACKET_BYTES);
|
|
packet[0] = 8; // echo request
|
|
packet[1] = 0;
|
|
packet[4] = (uint8_t)(ident >> 8);
|
|
packet[5] = (uint8_t)(ident & 0xFFu);
|
|
packet[6] = (uint8_t)(seq >> 8);
|
|
packet[7] = (uint8_t)(seq & 0xFFu);
|
|
|
|
for (uint32_t i = 0; i < PING_PAYLOAD_BYTES; i++)
|
|
packet[8 + i] = (uint8_t)('a' + (i % 26u));
|
|
|
|
uint16_t sum = icmp_checksum(packet, PING_PACKET_BYTES);
|
|
packet[2] = (uint8_t)(sum >> 8);
|
|
packet[3] = (uint8_t)(sum & 0xFFu);
|
|
}
|
|
|
|
static bool parse_echo_reply(const uint8_t* buf, size_t len, uint16_t ident, uint16_t seq,
|
|
size_t* payloadLenOut, uint8_t* typeOut, uint8_t* codeOut) {
|
|
size_t off = 0;
|
|
|
|
if (len >= 20 && (buf[0] >> 4) == 4) {
|
|
size_t ihl = (size_t)(buf[0] & 0x0Fu) * 4u;
|
|
if (ihl >= 20 && ihl <= len && buf[9] == SYS_IPPROTO_ICMP)
|
|
off = ihl;
|
|
}
|
|
|
|
if (len < off + 8)
|
|
return false;
|
|
|
|
uint8_t type = buf[off + 0];
|
|
uint8_t code = buf[off + 1];
|
|
if (typeOut) *typeOut = type;
|
|
if (codeOut) *codeOut = code;
|
|
|
|
uint16_t rid = ((uint16_t)buf[off + 4] << 8) | buf[off + 5];
|
|
uint16_t rseq = ((uint16_t)buf[off + 6] << 8) | buf[off + 7];
|
|
|
|
if (type == 0 && code == 0 && rid == ident && rseq == seq) {
|
|
if (payloadLenOut)
|
|
*payloadLenOut = len - off;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void usage(void) {
|
|
print("usage: apps/ping.exe [host-or-ipv4] [count]\n");
|
|
print("default: apps/ping.exe 10.0.2.2 4\n");
|
|
}
|
|
|
|
int main(void) {
|
|
char host[256];
|
|
char tok[256];
|
|
uint32_t count = PING_DEFAULT_COUNT;
|
|
strcpy(host, PING_DEFAULT_TARGET);
|
|
|
|
const char* cmd = SysGetPeb()->CommandLine.Data;
|
|
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
|
if (cmd) {
|
|
const char* rest = next_token(cmd, tok, sizeof(tok));
|
|
if (tok[0]) {
|
|
if (strlen(tok) >= sizeof(host)) {
|
|
print("ping: host too long\n");
|
|
SysExitProcess(1);
|
|
}
|
|
strcpy(host, tok);
|
|
cmd = rest;
|
|
}
|
|
if (cmd) {
|
|
(void)next_token(cmd, tok, sizeof(tok));
|
|
if (tok[0] && (!parse_u32(tok, &count) || count == 0 || count > 1000)) {
|
|
usage();
|
|
SysExitProcess(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t ip[4];
|
|
uint32_t st = resolve_host(host, ip);
|
|
if (st != SYS_STATUS_SUCCESS) {
|
|
print("ping: resolve failed "); print_u64(st); print(" for "); print(host); print("\n");
|
|
SysExitProcess(1);
|
|
}
|
|
|
|
char ipText[16];
|
|
format_ipv4(ipText, sizeof(ipText), ip);
|
|
|
|
uint32_t sock = 0;
|
|
st = SysCreateSocket(SYS_AF_INET, SYS_SOCK_RAW, SYS_IPPROTO_ICMP, &sock);
|
|
if (st != SYS_STATUS_SUCCESS) {
|
|
print("ping: SysCreateSocket(raw icmp) failed "); print_u64(st); print("\n");
|
|
SysExitProcess(1);
|
|
}
|
|
|
|
uint32_t timeout = PING_TIMEOUT_MS;
|
|
SYS_SOCKET_OPTION_REQUEST opt;
|
|
memset(&opt, 0, sizeof(opt));
|
|
opt.Option = SYS_SOCKET_OPT_RECV_TIMEOUT_MS;
|
|
opt.Value = &timeout;
|
|
opt.ValueSize = sizeof(timeout);
|
|
(void)SysSetSocketOption(sock, &opt);
|
|
|
|
SYS_SOCKET_ADDRESS_IPV4 remote;
|
|
memset(&remote, 0, sizeof(remote));
|
|
remote.Family = SYS_AF_INET;
|
|
remote.Port = 0;
|
|
remote.Address[0] = ip[0];
|
|
remote.Address[1] = ip[1];
|
|
remote.Address[2] = ip[2];
|
|
remote.Address[3] = ip[3];
|
|
|
|
print("PING "); print(host); print(" ("); print(ipText); print(") ");
|
|
print_u64(PING_PAYLOAD_BYTES); print(" data bytes\n");
|
|
|
|
uint16_t ident = (uint16_t)(SysGetCurrentPid() & 0xFFFFu);
|
|
uint32_t sentPackets = 0;
|
|
uint32_t receivedPackets = 0;
|
|
uint64_t minUs = 0, maxUs = 0, sumUs = 0;
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
uint16_t seq = (uint16_t)(i + 1u);
|
|
uint8_t packet[PING_PACKET_BYTES];
|
|
make_echo_request(packet, ident, seq);
|
|
|
|
size_t sent = 0;
|
|
SYS_SOCKET_SEND_TO_REQUEST sendReq;
|
|
memset(&sendReq, 0, sizeof(sendReq));
|
|
sendReq.Buffer = packet;
|
|
sendReq.Length = sizeof(packet);
|
|
sendReq.RemoteAddress = &remote;
|
|
sendReq.RemoteAddressSize = sizeof(remote);
|
|
sendReq.SentOut = &sent;
|
|
|
|
uint64_t start = SysGetTick();
|
|
st = SysSendToSocket(sock, &sendReq);
|
|
if (st != SYS_STATUS_SUCCESS) {
|
|
print("ping: send failed seq="); print_u64(seq); print(" status="); print_u64(st); print("\n");
|
|
if (i + 1 < count) SysSleep(1000);
|
|
continue;
|
|
}
|
|
sentPackets++;
|
|
|
|
bool matched = false;
|
|
uint8_t lastType = 0, lastCode = 0;
|
|
for (;;) {
|
|
uint8_t recvBuf[512];
|
|
SYS_SOCKET_ADDRESS_IPV4 from;
|
|
size_t fromSize = sizeof(from);
|
|
size_t got = 0;
|
|
SYS_SOCKET_RECV_FROM_REQUEST recvReq;
|
|
memset(&recvReq, 0, sizeof(recvReq));
|
|
recvReq.Buffer = recvBuf;
|
|
recvReq.Length = sizeof(recvBuf);
|
|
recvReq.RemoteAddressOut = &from;
|
|
recvReq.RemoteAddressSizeInOut = &fromSize;
|
|
recvReq.ReceivedOut = &got;
|
|
|
|
st = SysRecvFromSocket(sock, &recvReq);
|
|
if (st == SYS_STATUS_TIMEOUT)
|
|
break;
|
|
if (st != SYS_STATUS_SUCCESS) {
|
|
print("ping: recv failed status="); print_u64(st); print("\n");
|
|
break;
|
|
}
|
|
|
|
size_t icmpLen = 0;
|
|
if (parse_echo_reply(recvBuf, got, ident, seq, &icmpLen, &lastType, &lastCode)) {
|
|
uint64_t end = SysGetTick();
|
|
uint64_t deltaUs = end >= start ? end - start : 0;
|
|
receivedPackets++;
|
|
sumUs += deltaUs;
|
|
if (receivedPackets == 1 || deltaUs < minUs) minUs = deltaUs;
|
|
if (deltaUs > maxUs) maxUs = deltaUs;
|
|
|
|
print_u64(icmpLen); print(" bytes from "); print_ipv4(from.Address);
|
|
print(": icmp_seq="); print_u64(seq);
|
|
print(" time="); print_rtt_us(deltaUs); print(" ms\n");
|
|
matched = true;
|
|
break;
|
|
}
|
|
|
|
uint64_t now = SysGetTick();
|
|
if (now - start >= ((uint64_t)PING_TIMEOUT_MS * US_PER_MS))
|
|
break;
|
|
}
|
|
|
|
if (!matched) {
|
|
print("Request timeout for icmp_seq "); print_u64(seq);
|
|
if (lastType || lastCode) {
|
|
print(" last_type="); print_u64(lastType); print(" code="); print_u64(lastCode);
|
|
}
|
|
print("\n");
|
|
}
|
|
|
|
if (i + 1 < count)
|
|
SysSleep(1000);
|
|
}
|
|
|
|
print("--- "); print(host); print(" ping statistics ---\n");
|
|
print_u64(sentPackets); print(" packets transmitted, ");
|
|
print_u64(receivedPackets); print(" packets received, ");
|
|
uint32_t loss = sentPackets ? (uint32_t)(((uint64_t)(sentPackets - receivedPackets) * 100u) / sentPackets) : 100u;
|
|
print_u64(loss); print("% packet loss\n");
|
|
if (receivedPackets) {
|
|
print("round-trip min/avg/max = ");
|
|
print_rtt_us(minUs); print("/"); print_rtt_us(sumUs / receivedPackets); print("/"); print_rtt_us(maxUs); print(" ms\n");
|
|
}
|
|
|
|
SysCloseHandle(sock);
|
|
SysExitProcess(receivedPackets ? 0 : 1);
|
|
return receivedPackets ? 0 : 1;
|
|
}
|