Commit 981898e5 authored by Peter H. Jin's avatar Peter H. Jin
Browse files

Initial commit

parents
traceroute-maker
traceroute-maker: traceroute-maker.c compute_result.c
gcc -o $@ $^
clean:
rm -f traceroute-maker
#include <stdint.h>
#define TR_SKIPLOU 0x10
int decode_word(uint16_t word, uint8_t *result) {
result[0] = (word) & 31;
result[1] = (word >> 5) & 31;
result[2] = (word >> 10) & 31;
return __builtin_parity(word);
}
int compute_packet(uint8_t tr_class, uint16_t word_1, uint16_t word_2, uint8_t hlim, uint32_t *result) {
switch(tr_class & 0xf0) {
case TR_SKIPLOU:
/* If hlim / 4 == 6 and hlim mod 4 == 0, output a fixed verse */
/* If hlim / 4 >= 6 and hlim mod 4 != 0, output the terminator */
/* If hlim mod 4 == 0, output a fixed verse */
/* If hlim mod 4 != 0, output a selected verse */
;uint8_t word1_s[3] = {0};
;uint8_t word2_s[3] = {0};
int parity_w1 = decode_word(word_1 ^ 0x55aa, word1_s);
int parity_w2 = decode_word(word_2 ^ 0xa5a5, word2_s);
if (parity_w1 || !parity_w2) {
*result = 0x80000000;
return 1;
}
uint8_t major_selector = hlim >> 2;
uint8_t minor_selector = hlim & 3;
if (major_selector >= 6) {
if (hlim == 24) {
*result = 0x800000f0;
return 2;
} else {
*result = 0x800000f1;
return 1;
}
}
if (minor_selector == 0) {
*result = 0x800000f0;
return 2;
}
uint8_t values[] = {word2_s[0], word2_s[1], word2_s[2], word1_s[0], word1_s[1], word1_s[2]};
uint8_t rvalue = values[major_selector];
if (rvalue == 0x1f) return 0;
*result = 0x80000020U | rvalue;
return 2;
}
return 0;
}
<!DOCTYPE html>
<html>
<head>
<title>Traceroute Maker</title>
</head>
<body onload="__trm_selector_handler();">
<h1>Traceroute Maker</h1>
<p>
If you've heard about the <a href="https://news.ycombinator.com/item?id=5192656">Star Wars traceroute</a>
that happened several years ago, then here's something very similar, but it's not just one traceroute
anymore. Instead, you can choose which lines to display in the traceroute, so there are effectively more
than 190 million possible traceroutes just for Skip to my Lou alone, with a theoretical maximum of just
over 1 billion per song.
</p>
<p>
Select a song:
<select id="song" onchange="__trm_selector_handler();">
<option value="select" selected="1">Select:</option>
<option value="_10">Skip to my Lou</option>
<option value="_20">If You're Happy and You Know It (not yet operational)</option>
<option value="_30">BINGO (There was a farmer, had a dog...) (not yet operational)</option>
<option value="_40">Old MacDonald Had a Farm (not yet operational)</option>
<option value="_50">She'll be Coming 'round the Mountain (not yet operational)</option>
<option value="_60">This Old Man (not yet operational)</option>
</select>
</p>
<p id="verseForm"></p>
<div style="background-color: #ff8;">
<div id="ipOut"></div>
<div id="lgText" style="display: none;">
You can view this in a looking glass:
<ul>
<li><a id="telia-lg" href="https://lg.telia.net">Telia</a></li>
<li><a href="https://lg.he.net">Hurricane Electric</a></li>
<li><a id="lb-lg" href="https://lg.as3280.net">LayerBridge, has direct peering to EVIX</a></li>
</ul>
</div>
</div>
<script src="frontend.js"></script>
<p><a href="https://www.peterjin.org/wiki/Traceroute_Maker">About this project.</a></p>
</body>
</html>
function __trm_make_span(id, text) {
let r = document.createElement('span');
if (id) r.id = id;
if (text) r.innerHTML = text;
return r;
}
function __trm_dropdown_generator(id, items, selectMap, selected) {
let item = document.createElement("select");
if (id) item.setAttribute("id", id);
let counter = 0;
for (let y of (selectMap ? selectMap(id) : items)) {
let x = selectMap ? items[y] : y;
let i = document.createElement("option");
if (Array.isArray(x)) {
i.setAttribute("value", x[1]);
i.innerHTML = x[0];
} else {
i.setAttribute("value", String(counter));
i.innerHTML = x;
}
if (selected == counter) i.setAttribute("selected", true);
counter++;
item.add(i);
}
return item;
}
function __trm_retrieve_verse(key, forceNum){
let e = document.getElementById(key);
if (!e) return 0;
let r = e.options[e.selectedIndex].value || 0;
if (forceNum) return Number(r);
return r;
}
function __trm_submit(prefix) {
let c = String(__trm_retrieve_verse("song"));
if (!c) return;
let b = __trm_selectors[c];
if (!b) return;
let r = b.ip_maker();
let hex = "0123456789abcdef";
let w1 = "";
let w2 = "";
w1 += hex[((r >> 28) & 15) ^ 5];
w1 += hex[((r >> 24) & 15) ^ 5];
w1 += hex[((r >> 20) & 15) ^ 10];
w1 += hex[((r >> 16) & 15) ^ 10];
w2 += hex[((r >> 12) & 15) ^ 10];
w2 += hex[((r >> 8) & 15) ^ 5];
w2 += hex[((r >> 4) & 15) ^ 10];
w2 += hex[(r & 15) ^ 5];
while (w1.length > 1 && w1[0] == '0') w1 = w1.substring(1);
while (w2.length > 1 && w2[0] == '0') w2 = w2.substring(1);
let resultIP = prefix + w1 + ":" + w2;
document.getElementById('telia-lg').setAttribute("href", "https://lg.telia.net/?type=trace&router=las-b24&address=" + resultIP);
document.getElementById('lb-lg').setAttribute("href", "https://lg.as3280.net/?command=trace&protocol=ipv6&query=" + resultIP + "&router=border");
document.getElementById('ipOut').innerHTML = "Your IP address to traceroute to is: " + resultIP +
"<br /><br />View this in your terminal:<br /><code>tracert -h 60 -w 1 " + resultIP +
"</code> (Windows)<br /><code>traceroute -m 60 -w 1 " + resultIP + "</code> (Mac/Linux)" +
"<br /><br /><a href=" + '"javascript:document.getElementById(\u0027lgText\u0027).style.display = \u0027block\u0027; void(0);">Can\u0027t view this in your terminal?</a>';
}
function __trm_page_generator_closure(prefix, verses, defaultVerse, prefixGen, suffixGen, selectMap) {
return function(p_elem) {
if (!p_elem) return;
for (let v of [["First", 0], ["Second", 1], ["Third", 2], ["Fourth", 3], ["Fifth", 4], ["Sixth", 5]]) {
let pElement = document.createElement('p');
pElement.appendChild(__trm_make_span(null, prefixGen ? prefixGen(v[1]) : (v[0] + " verse: ")));
pElement.appendChild(__trm_dropdown_generator("__trm_skiplou_dropdown_" + v[1], verses, selectMap, defaultVerse[v[1]]));
if (suffixGen) pElement.appendChild(__trm_make_span(null, suffixGen(v[1])));
p_elem.appendChild(pElement);
}
p_elem.appendChild(__trm_make_span(null, '<button onclick="__trm_submit(\u0027' + prefix + '\u0027)">Generate a traceroute!</button>'));
};
}
function __trm_generic_ip_maker() {
let word_l = Number(__trm_retrieve_verse("__trm_skiplou_dropdown_0"));
word_l |= __trm_retrieve_verse("__trm_skiplou_dropdown_1") << 5;
word_l |= __trm_retrieve_verse("__trm_skiplou_dropdown_2") << 10;
word_l |= __trm_calculate_parity(word_l, 1) ? 0 : 32768;
word_l |= __trm_retrieve_verse("__trm_skiplou_dropdown_3") << 16;
word_l |= __trm_retrieve_verse("__trm_skiplou_dropdown_4") << 21;
word_l |= __trm_retrieve_verse("__trm_skiplou_dropdown_5") << 26;
word_l |= __trm_calculate_parity(word_l, 1) ? 0 : 2147483648;
return word_l;
}
var __trm_selectors = {
"_10": {
page_generator: __trm_page_generator_closure("2602:806:a003:40f:0:110:",
["Skip, skip, skip to my lou",
"Lou, lou, skip to my lou",
"Found my partner, skip to my lou",
"Lost my partner, what'll I do",
"Can't get a red bird, blue one'll do",
"Can't get a red bird, blue bird'll do",
"Little red wagon, paint it blue",
"Little red wagon painted blue",
"I'll find another one as pretty as you",
"I'll get another one as pretty as you",
"I'll find another one prettier than you",
"I'll get another one prettier than you",
"Cat's in the cream jar, ooh ooh ooh",
"Cat's in the cream jar, what'll I do",
"Cows in the pasture, moo moo moo",
"Cows in the pasture, two by two",
"Fly's in the buttermilk, shoo-fly-shoo",
"Fly's in the buttermilk, shoo shoo shoo",
"Fly's in the sugar bowl, shoo-fly-shoo",
"Fly's in the sugar bowl, shoo shoo shoo",
"IPv6, I have it too",
"No IP6, so boo boo boo",
"More addressing as my network grew",
["* * *", 31]],
[0,16,0,6,8,0], null, null),
ip_maker: __trm_generic_ip_maker
},
"_20": {
page_generator: __trm_page_generator_closure("2602:806:a003:40f:0:120:", [
"clap your hands",
"stomp your feet",
"shout hurray",
"nod your head",
"jump up high",
"do all of the above",
["* * *", 31]],
[0, 1, 2, 3, 4, 5], function(a) {return "If you're happy and you know it, ";}, null),
ip_maker: __trm_generic_ip_maker
},
"_50": {
page_generator: __trm_page_generator_closure("2602:806:a003:40f:0:150:", [
"coming 'round the mountain",
"driving six white horses",
"wearing pink pajamas",
"wearing red pajamas",
"wearing green pajamas",
"wearing blue pajamas",
"go out to meet her",
"sing Hallelujah",
"have chicken and dumplings",
["* * *", 31]],
[0, 1, 2, 0, 2, 1], function(a) {return a < 3 ? "She'll be " : "Oh, we'll all ";}, function(a) {return " when she comes";},
function (id) {
if (id < "__trm_skiplou_dropdown_2a") {
return [0, 1, 2, 3, 4, 5, 9];
} else {
return [6, 7, 8, 9];
}
}),
ip_maker: __trm_generic_ip_maker
}
}
function __trm_calculate_parity(word, odd) {
let s = 0;
for (let i = 0; i < 32; i++) {
s ^= (word >> i) & 1;
}
return s ^ !odd;
}
function __trm_selector_handler() {
let i = document.getElementById("song");
if (!i) return;
let v = document.getElementById("verseForm");
if (!v) return;
v.innerHTML = "";
let o = i.options[i.selectedIndex].value;
if (o && __trm_selectors[o]) __trm_selectors[o].page_generator(v);
}
/* magic-traceroute.c by Peter H. Jin, for peterjin.org. Public domain. */
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <stdint.h>
#include <linux/seccomp.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <unistd.h>
#include <linux/prctl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/icmp6.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <time.h>
#include <sys/stat.h>
#define MAXIMUM_PACKET_SIZE 1280
struct incoming_packet {
struct ip6_hdr v6hdr;
union {
struct tcphdr tcphdr;
struct udphdr udphdr;
struct icmp6_hdr icmp6hdr;
struct {
struct icmp6_hdr header;
char data[MAXIMUM_PACKET_SIZE];
} icmp6data;
uint8_t xdata[MAXIMUM_PACKET_SIZE];
} data;
};
int compute_packet(uint8_t tr_class, uint16_t word_1, uint16_t word_2, uint8_t hlim, uint32_t *result);
static uint16_t ip_checksum(const uint16_t *packet_a, unsigned int nr_words_a, const uint16_t *packet_b, unsigned int nr_words_b) {
uint32_t accum = 0;
for (unsigned int i = 0; i < nr_words_a; i++) {
accum += ntohs(packet_a[i]);
}
for (unsigned int i = 0; i < nr_words_b; i++) {
accum += ntohs(packet_b[i]);
}
return htons(0xffff - ((accum >> 16) + (accum & 0xffff)));
}
int main(int argc, char **argv) {
int o = -1;
const char *tun_device = "/dev/net/tun";
int tun_fd = -1;
const char *post_tun_cmd = NULL;
const char *devname = NULL; /* tracert_tun */
struct in6_addr range = {{0}};
range.s6_addr32[0] = htonl(0xfe800000);
while ((o = getopt(argc, argv, "t:f:c:d:r:")) != -1) {
switch(o) {
case 't':
tun_device = optarg;
break;
case 'f':
tun_fd = atoi(optarg);
break;
case 'c':
post_tun_cmd = optarg;
break;
case 'd':
devname = optarg;
break;
case 'r':
if (inet_pton(AF_INET6, optarg, &range) != 1) {
fprintf(stderr, "Invalid IPv6 address %s\n", optarg);
exit(1);
}
break;
default:
fprintf(stderr, "%s [-t /dev/net/tun] [-f tun_fd] [-c post_tun_cmd] [-d devname] [-p fe80::]\n", argv[0]);
exit(1);
break;
}
}
if (tun_fd == -1) {
tun_fd = open(tun_device, O_RDWR | O_CLOEXEC | O_NOCTTY);
if (tun_fd == -1) {
perror(tun_device);
return 1;
}
struct ifreq ifr = {0};
ifr.ifr_flags = IFF_TUN|IFF_NO_PI;
if (devname) strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name));
if (ioctl(tun_fd, TUNSETIFF, &ifr) < 0) {
perror("TUNSETIFF");
return 1;
}
setenv("MAGIC_TRACEROUTE_TUN_NAME", ifr.ifr_name, 1);
}
if (post_tun_cmd) {
if (system(post_tun_cmd)) {
perror("failed to execute command");
return 1;
}
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, 0, 0, 0)) {
perror("cannot set seccomp strict mode");
return 1;
}
while (1) {
struct incoming_packet p = {0};
ssize_t orig_len = read(tun_fd, &p, sizeof(p));
if (orig_len <= 0) {
/* This should never happen, but nanosleep is disallowed by seccomp.
* Should use timerfd instead. */
nanosleep(&(struct timespec) {0, 10000000}, NULL);
continue;
}
if ((p.v6hdr.ip6_vfc & 0xf0) != 0x60) continue;
if (p.v6hdr.ip6_dst.s6_addr32[0] != range.s6_addr32[0]) continue;
if (p.v6hdr.ip6_dst.s6_addr32[1] != range.s6_addr32[1]) continue;
// if (p.v6hdr.ip6_dst.s6_addr32[2] != range.s6_addr32[2]) continue;
if (p.v6hdr.ip6_src.s6_addr32[0] == range.s6_addr32[0])
if (p.v6hdr.ip6_src.s6_addr32[1] == range.s6_addr32[1])
if (p.v6hdr.ip6_src.s6_addr16[4] == range.s6_addr16[4]) continue;
if (p.v6hdr.ip6_nxt == IPPROTO_TCP
|| p.v6hdr.ip6_nxt == IPPROTO_UDP
|| (p.v6hdr.ip6_nxt == IPPROTO_ICMPV6
&& p.data.icmp6hdr.icmp6_type == ICMP6_ECHO_REQUEST)) {
} else continue;
struct incoming_packet new_p = {0};
new_p.v6hdr.ip6_vfc = 0x60;
new_p.v6hdr.ip6_hlim = 0x60;
new_p.v6hdr.ip6_dst = p.v6hdr.ip6_src;
new_p.v6hdr.ip6_src = p.v6hdr.ip6_dst;
int compute_result = compute_packet(p.v6hdr.ip6_dst.s6_addr[11], ntohs(p.v6hdr.ip6_dst.s6_addr16[6]), ntohs(p.v6hdr.ip6_dst.s6_addr16[7]), p.v6hdr.ip6_hlim, &new_p.v6hdr.ip6_src.s6_addr32[3]);
new_p.v6hdr.ip6_src.s6_addr32[3] = htonl(new_p.v6hdr.ip6_src.s6_addr32[3]);
if (compute_result <= 0) continue;
if (compute_result > 2) continue;
new_p.v6hdr.ip6_src.s6_addr[11] = 0;
new_p.v6hdr.ip6_nxt = IPPROTO_ICMPV6;
new_p.data.icmp6hdr.icmp6_type = compute_result == 2 ? ICMP6_TIME_EXCEEDED : ICMP6_DST_UNREACH;
if (compute_result == 2) {
p.v6hdr.ip6_hlim = 1;
}
memcpy(new_p.data.icmp6data.data, &p, MAXIMUM_PACKET_SIZE);
struct {
struct in6_addr src;
struct in6_addr dst;
uint32_t pl;
uint16_t z0;
uint8_t z1;
uint8_t nh;
} cshdr = {0};
cshdr.src = new_p.v6hdr.ip6_src;
cshdr.dst = new_p.v6hdr.ip6_dst;
cshdr.nh = IPPROTO_ICMPV6;
size_t maxlen = offsetof(struct incoming_packet, data) + sizeof(new_p.data.icmp6hdr);
size_t addl_len = orig_len;
if (addl_len >= MAXIMUM_PACKET_SIZE) addl_len = MAXIMUM_PACKET_SIZE;
cshdr.pl = htonl(sizeof(new_p.data.icmp6hdr) + addl_len);
new_p.data.icmp6hdr.icmp6_cksum = ip_checksum((uint16_t *) &cshdr, sizeof(cshdr) / 2, (uint16_t *) &new_p.data.icmp6data, (sizeof(new_p.data.icmp6hdr) + addl_len + 1) / 2);
new_p.v6hdr.ip6_plen = htons(maxlen + addl_len - sizeof(struct ip6_hdr));
write(tun_fd, &new_p, maxlen + addl_len);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment