diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | c/dhcp.c | 72 | ||||
| -rw-r--r-- | c/sh.c | 6 | ||||
| -rw-r--r-- | c/sock.h | 7 | ||||
| -rw-r--r-- | c/tcp.c | 107 | ||||
| -rw-r--r-- | include/gbos.inc | 2 | ||||
| -rw-r--r-- | src/programs.asm | 7 | ||||
| -rw-r--r-- | src/socket.asm | 97 | ||||
| -rwxr-xr-x | tools/netboot | 45 | ||||
| -rw-r--r-- | tools/tunbridge.py | 49 |
10 files changed, 360 insertions, 34 deletions
@@ -20,7 +20,7 @@ $(BUILD)/%.o: src/%.asm | $(BUILD) # C programs: compiled with SDCC into ROM-bank blobs and INCBIN'd by programs.asm CBLOBS := c/chello.bin c/echo.bin c/true.bin c/false.bin c/uname.bin \ c/pid.bin c/cat.bin c/wc.bin c/head.bin c/args.bin \ - c/ls.bin c/save.bin c/rm.bin c/sh.bin c/ps.bin c/kill.bin c/spin.bin c/blk.bin c/mkdir.bin c/count.bin c/ptest.bin c/necho.bin c/wget.bin c/chat.bin c/netd.bin c/ping.bin c/nslookup.bin + c/ls.bin c/save.bin c/rm.bin c/sh.bin c/ps.bin c/kill.bin c/spin.bin c/blk.bin c/mkdir.bin c/count.bin c/ptest.bin c/necho.bin c/wget.bin c/chat.bin c/netd.bin c/ping.bin c/nslookup.bin c/dhcp.bin $(BUILD)/programs.o: $(CBLOBS) # C programs also depend on the shared libc sources diff --git a/c/dhcp.c b/c/dhcp.c new file mode 100644 index 0000000..e4186f4 --- /dev/null +++ b/c/dhcp.c @@ -0,0 +1,72 @@ +#include "sock.h" +/* dhcp - a minimal DHCP client. DISCOVER -> OFFER -> REQUEST -> ACK over UDP + (kernel owns UDP; this owns BOOTP/DHCP), then hands the leased address to the + kernel with net_setip(). Run at boot before the shell. Times out gracefully + if there's no server (the shell then comes up unconfigured). */ + +static unsigned char pkt[288]; /* BOOTP is ~240B + options (frames > 255) */ +static unsigned char ip[4]; +static unsigned char bcast[4]; + +/* fill the fixed BOOTP request header + magic cookie; options start at 240 */ +static void bootp(void) { + unsigned int i; + for (i = 0; i < 240; i++) pkt[i] = 0; + pkt[0] = 1; pkt[1] = 1; pkt[2] = 6; /* op=request htype=eth hlen=6 */ + pkt[4] = 0x12; pkt[5] = 0x34; pkt[6] = 0x56; pkt[7] = 0x78; /* xid */ + pkt[10] = 0x80; /* flags: broadcast reply */ + pkt[28] = 0x02; pkt[29] = 0x47; pkt[30] = 0x42; pkt[33] = 0x01; /* client MAC */ + pkt[236] = 0x63; pkt[237] = 0x82; pkt[238] = 0x53; pkt[239] = 0x63; /* magic */ +} + +/* DHCP message type from the received packet (option 53), or 0 */ +static unsigned char mtype(void) { + unsigned int p = 240; + while (p < 280 && pkt[p] != 255) { + if (pkt[p] == 0) { p++; continue; } /* pad */ + if (pkt[p] == 53) return pkt[p + 2]; + p += 2 + pkt[p + 1]; + } + return 0; +} + +void main(void) { + unsigned char s, i, n; + unsigned int p; + bcast[0] = 255; bcast[1] = 255; bcast[2] = 255; bcast[3] = 255; + + puts("dhcp: discovering"); nl(); + s = net_socket(SOCK_UDP); + if (s == 0xFF) { puts("dhcp: no socket"); nl(); return; } + net_bind(s, 68); + net_connect(s, bcast, 67); + + /* DISCOVER -> OFFER (retry a few times for startup timing) */ + bootp(); + p = 240; + pkt[p++] = 53; pkt[p++] = 1; pkt[p++] = 1; /* DHCPDISCOVER */ + pkt[p++] = 255; + n = 0xFF; + for (i = 0; i < 3 && n == 0xFF; i++) { + net_send(s, pkt, (unsigned char)p); + n = net_recv(s, pkt, 255); + } + if (n == 0xFF || mtype() != 2) { puts("dhcp: no offer"); nl(); net_close(s); return; } + ip[0] = pkt[16]; ip[1] = pkt[17]; ip[2] = pkt[18]; ip[3] = pkt[19]; /* yiaddr */ + + /* REQUEST -> ACK */ + bootp(); + p = 240; + pkt[p++] = 53; pkt[p++] = 1; pkt[p++] = 3; /* DHCPREQUEST */ + pkt[p++] = 50; pkt[p++] = 4; /* requested IP */ + pkt[p++] = ip[0]; pkt[p++] = ip[1]; pkt[p++] = ip[2]; pkt[p++] = ip[3]; + pkt[p++] = 255; + net_send(s, pkt, (unsigned char)p); + n = net_recv(s, pkt, 255); + net_close(s); + if (n == 0xFF || mtype() != 5) { puts("dhcp: request failed"); nl(); return; } + ip[0] = pkt[16]; ip[1] = pkt[17]; ip[2] = pkt[18]; ip[3] = pkt[19]; + + net_setip(ip); + puts("dhcp: leased "); for (i = 0; i < 4; i++) { putu(ip[i]); if (i < 3) putc('.'); } nl(); +} @@ -155,6 +155,12 @@ void main(void) { char *tok[16]; char cwd[40]; cwd[0] = '/'; cwd[1] = 0; + /* bring up networking first: run the DHCP client and wait for it to finish + (got a lease, or gave up). Only then drop to the prompt. */ + { + unsigned char d = lookup("dhcp"); + if (d != NOFD) { if (fork() == 0) exec(d); else wait(); } + } for (;;) { unsigned char nt, i, pipepos, c, bg; /* reap finished background jobs */ @@ -55,4 +55,11 @@ static void net_close(unsigned char s) { static void net_poll(void) { _nr.op = 6; sys_net(&_nr); } +/* set our IPv4 address (the DHCP client calls this once it has a lease) */ +static void net_setip(const unsigned char *ip) { + unsigned char i; + _nr.op = 7; + for (i = 0; i < 4; i++) _nr.ip[i] = ip[i]; + sys_net(&_nr); +} #endif @@ -0,0 +1,107 @@ +#include "netlib.h" +/* tcp - a minimal TCP client over SLIP (milestone C). Connects to 10.0.0.1:7, + sends a line, prints the reply, closes. No retransmission: the GB<->gateway + link is lossless, and the gateway's real socket handles the lossy side. + Us = 10.0.0.2, peer = 10.0.0.1. */ + +static unsigned char pkt[256]; +static unsigned long snd_nxt, rcv_nxt; +#define LPORT 40000u +#define RPORT 7u + +/* SYN 0x02, ACK 0x10, FIN 0x01, PSH 0x08, RST 0x04 */ + +static unsigned int sum16(unsigned char *d, unsigned int len, unsigned int sum) { + unsigned int w, i = 0; + while (i + 1 < len) { w = ((unsigned int)d[i] << 8) | d[i + 1]; sum += w; if (sum < w) sum++; i += 2; } + if (len & 1) { w = (unsigned int)d[len - 1] << 8; sum += w; if (sum < w) sum++; } + return sum; +} +static void put32(unsigned char *p, unsigned long v) { + p[0] = (unsigned char)(v >> 24); p[1] = (unsigned char)(v >> 16); + p[2] = (unsigned char)(v >> 8); p[3] = (unsigned char)v; +} +static unsigned long get32(unsigned char *p) { + return ((unsigned long)p[0] << 24) | ((unsigned long)p[1] << 16) + | ((unsigned long)p[2] << 8) | p[3]; +} + +/* build+send IP+TCP with 'flags' and 'dlen' payload bytes already at pkt[40..] */ +static void tcp_send(unsigned char flags, unsigned char dlen) { + unsigned int c, psum, tcplen = 20 + dlen, total = 20 + tcplen; + pkt[0] = 0x45; pkt[1] = 0; pkt[2] = (unsigned char)(total >> 8); pkt[3] = (unsigned char)total; + pkt[4] = 0; pkt[5] = 1; pkt[6] = 0x40; pkt[7] = 0; pkt[8] = 64; pkt[9] = 6; + pkt[10] = 0; pkt[11] = 0; + pkt[12] = 10; pkt[13] = 0; pkt[14] = 0; pkt[15] = 2; + pkt[16] = 10; pkt[17] = 0; pkt[18] = 0; pkt[19] = 1; + pkt[20] = LPORT >> 8; pkt[21] = (unsigned char)LPORT; + pkt[22] = RPORT >> 8; pkt[23] = (unsigned char)RPORT; + put32(pkt + 24, snd_nxt); + put32(pkt + 28, rcv_nxt); + pkt[32] = 0x50; pkt[33] = flags; pkt[34] = 2; pkt[35] = 0; /* window 512 */ + pkt[36] = 0; pkt[37] = 0; pkt[38] = 0; pkt[39] = 0; + psum = sum16(pkt + 12, 8, 0); + c = 6; psum += c; if (psum < c) psum++; + c = tcplen; psum += c; if (psum < c) psum++; + c = ~sum16(pkt + 20, tcplen, psum); + pkt[36] = (unsigned char)(c >> 8); pkt[37] = (unsigned char)c; + c = ~sum16(pkt, 20, 0); + pkt[10] = (unsigned char)(c >> 8); pkt[11] = (unsigned char)c; + slip_send((char *)pkt, (unsigned char)total); +} + +/* receive the next TCP segment for our connection; returns TCP header offset, + sets *flags,*seq,*dlen,*doff. */ +static unsigned char tcp_recv(unsigned char *flags, unsigned long *seq, + unsigned char *dlen, unsigned char *doff) { + unsigned char len, ihl, hl; + for (;;) { + len = slip_recv((char *)pkt, 255); + if (len < 40 || (pkt[0] >> 4) != 4 || pkt[9] != 6) continue; + ihl = (pkt[0] & 0x0f) << 2; + if (((unsigned int)pkt[22] << 8 | pkt[23]) != LPORT) continue; /* not our port */ + hl = (pkt[ihl + 12] >> 4) << 2; + *flags = pkt[ihl + 13]; + *seq = get32(pkt + ihl + 4); + *doff = (unsigned char)(ihl + hl); + *dlen = (unsigned char)(len - ihl - hl); + return ihl; + } +} + +void main(void) { + unsigned char flags, dlen, doff, i; + unsigned long seq; + puts("tcp: SYN -> 10.0.0.1:7"); nl(); + snd_nxt = 1000; rcv_nxt = 0; + tcp_send(0x02, 0); /* SYN */ + snd_nxt = 1001; + + tcp_recv(&flags, &seq, &dlen, &doff); /* expect SYN|ACK */ + if (!(flags & 0x02)) { puts("tcp: no SYN-ACK"); nl(); return; } + rcv_nxt = seq + 1; + tcp_send(0x10, 0); /* ACK -> ESTABLISHED */ + + pkt[40] = 'h'; pkt[41] = 'i'; pkt[42] = ' '; pkt[43] = 'g'; pkt[44] = 'b'; + tcp_send(0x18, 5); /* PSH|ACK "hi gb" */ + snd_nxt += 5; + + for (;;) { + tcp_recv(&flags, &seq, &dlen, &doff); + if (dlen) { + puts("tcp: recv '"); + for (i = 0; i < dlen; i++) putc(pkt[doff + i]); + puts("'"); nl(); + rcv_nxt = seq + dlen; + tcp_send(0x10, 0); /* ACK the data */ + tcp_send(0x11, 0); /* FIN|ACK - start closing */ + snd_nxt += 1; + } + if (flags & 0x01) { /* their FIN */ + rcv_nxt = seq + dlen + 1; + tcp_send(0x10, 0); /* ACK it */ + puts("tcp: closed"); nl(); + return; + } + } +} diff --git a/include/gbos.inc b/include/gbos.inc index 049a4bc..937d083 100644 --- a/include/gbos.inc +++ b/include/gbos.inc @@ -152,6 +152,7 @@ DEF NET_RECV EQU 3 ; NR_SOCK, NR_BUF, NR_LEN(max) -> A=len or $FF(timeout); DEF NET_CLOSE EQU 4 ; NR_SOCK DEF NET_BIND EQU 5 ; NR_SOCK, NR_PORT -> bind local port (UDP) DEF NET_POLL EQU 6 ; pump RX once (answer pings) -> A=0 +DEF NET_SETIP EQU 7 ; set our IPv4 address from NR_IP (DHCP) ; socket types DEF SOCK_ICMP EQU 1 DEF SOCK_UDP EQU 2 @@ -252,5 +253,6 @@ DEF PROG_CHAT EQU 25 DEF PROG_NETD EQU 26 DEF PROG_PING EQU 27 DEF PROG_NSLOOKUP EQU 28 +DEF PROG_DHCP EQU 29 ENDC diff --git a/src/programs.asm b/src/programs.asm index 9ae7f58..e89fe0c 100644 --- a/src/programs.asm +++ b/src/programs.asm @@ -127,6 +127,9 @@ ProgPing: SECTION "prog_nslookup", ROMX[$4000], BANK[30] ProgNslookup: INCBIN "c/nslookup.bin" +SECTION "prog_dhcp", ROMX[$4000], BANK[31] +ProgDhcp: + INCBIN "c/dhcp.bin" ; ----------------------------------------------------------------------------- ; PROG_SH (bank 3) - the shell, now written in C (c/sh.c): parses >/</| and @@ -227,6 +230,8 @@ ProgramTable:: dw ProgPing db LOW(BANK(ProgNslookup)), HIGH(BANK(ProgNslookup)) dw ProgNslookup + db LOW(BANK(ProgDhcp)), HIGH(BANK(ProgDhcp)) + dw ProgDhcp ; ----------------------------------------------------------------------------- ; NameTable (ROM0) - command name -> program id, searched by sys_lookup. @@ -291,4 +296,6 @@ NameTable:: db PROG_PING db "nslookup", 0 db PROG_NSLOOKUP + db "dhcp", 0 + db PROG_DHCP db 0 diff --git a/src/socket.asm b/src/socket.asm index 9ce1daa..3b2b0c9 100644 --- a/src/socket.asm +++ b/src/socket.asm @@ -21,11 +21,11 @@ DEF SK_HASRX EQU 9 ; 1 rxbuf holds a datagram DEF SK_RXLEN EQU 10 ; 1 payload length DEF SK_SRCIP EQU 11 ; 4 sender IP DEF SK_SRCPORT EQU 15 ; 2 sender port -DEF SK_RXBUF EQU 17 ; 208 payload (>= one max segment at MSS 200) -DEF SK_STATE EQU 225 ; 1 TCP state (see TCP_* below) -DEF SK_SND EQU 226 ; 4 snd_nxt (our next sequence number) -DEF SK_RCV EQU 230 ; 4 rcv_nxt (next sequence we expect from peer) -DEF SK_SIZE EQU 234 +DEF SK_RXBUF EQU 17 ; 288 payload (holds a DHCP reply, or a max segment) +DEF SK_STATE EQU 305 ; 1 TCP state (see TCP_* below) +DEF SK_SND EQU 306 ; 4 snd_nxt (our next sequence number) +DEF SK_RCV EQU 310 ; 4 rcv_nxt (next sequence we expect from peer) +DEF SK_SIZE EQU 314 DEF TCP_MSS EQU 200 ; TCP connection states @@ -48,9 +48,9 @@ wNetReq:: DS NR_SIZE wNetSockPtr:: DS 2 wNetIhl:: DS 1 wNetSeq:: DS 1 -wNetTx:: DS 256 ; tx build buffer -wNetRxBuf:: DS 256 ; rx frame assembly / parse -wNetRxLen:: DS 1 +wNetTx:: DS 320 ; tx build buffer +wNetRxBuf:: DS 320 ; rx frame assembly / parse +wNetRxLen:: DS 2 ; 16-bit: frames can exceed 255 bytes (DHCP/BOOTP) wNetRxEsc:: DS 1 wNetUdpPort:: DS 2 ; scratch: UDP dst port being demuxed wNetTO:: DS 3 ; recv timeout counter (net_pump clobbers registers) @@ -74,19 +74,31 @@ net_init:: ld a, b or c jr nz, .clr - ld a, 10 - ld [wNetOurIP+0], a xor a + ld [wNetOurIP+0], a ; 0.0.0.0 until DHCP assigns an address ld [wNetOurIP+1], a ld [wNetOurIP+2], a - ld a, 2 ld [wNetOurIP+3], a - xor a ld [wNetRxLen], a + ld [wNetRxLen+1], a ld [wNetRxEsc], a ld [wNetSeq], a ret +; net_setip - store our IPv4 address (from wNetReq.NR_IP). Used by the DHCP +; client once it has a lease. +net_op_setip: + ld a, [wNetReq+NR_IP+0] + ld [wNetOurIP+0], a + ld a, [wNetReq+NR_IP+1] + ld [wNetOurIP+1], a + ld a, [wNetReq+NR_IP+2] + ld [wNetOurIP+2], a + ld a, [wNetReq+NR_IP+3] + ld [wNetOurIP+3], a + xor a + ret + ; ----------------------------------------------------------------------------- ; low-level link port (byte at a time). Preserve BC/DE/HL; use A only. net_putbyte: ; A = byte to transmit @@ -113,13 +125,13 @@ net_getbyte_nb: ; -> A = byte, CF set if none ret ; ----------------------------------------------------------------------------- -; net_slip_send(HL = buf, B = len) - transmit a SLIP frame. +; net_slip_send(HL = buf, DE = len) - transmit a SLIP frame (len may exceed 255). net_slip_send: ld a, SLIP_END call net_putbyte .loop - ld a, b - or a + ld a, d + or e jr z, .end ld a, [hl+] cp SLIP_END @@ -128,7 +140,7 @@ net_slip_send: jr z, .esc_esc call net_putbyte .cont - dec b + dec de jr .loop .esc_end ld a, SLIP_ESC @@ -235,6 +247,8 @@ sys_net:: jp z, net_op_bind cp NET_POLL jp z, net_op_poll + cp NET_SETIP + jp z, net_op_setip ld a, $FF ret @@ -580,7 +594,8 @@ icmp_send: ; transmit: 28 + payload ld a, [wNetReq+NR_LEN] add 28 - ld b, a + ld e, a + ld d, 0 ld hl, wNetTx call net_slip_send xor a @@ -594,10 +609,12 @@ udp_send: ld [wNetTx+0], a xor a ld [wNetTx+1], a - ld [wNetTx+2], a ld a, [wNetReq+NR_LEN] add 28 ; 20 IP + 8 UDP + payload - ld [wNetTx+3], a + ld [wNetTx+3], a ; IP total length low + ld a, 0 + adc 0 + ld [wNetTx+2], a ; IP total length high (carry, for DHCP) xor a ld [wNetTx+4], a ld [wNetTx+5], a @@ -727,7 +744,10 @@ udp_send: ld [wNetTx+11], a ld a, [wNetReq+NR_LEN] add 28 - ld b, a + ld e, a + ld a, 0 + adc 0 + ld d, a ld hl, wNetTx call net_slip_send xor a @@ -774,20 +794,32 @@ net_pump: .store ld a, c .store2 - ; store A at wNetRxBuf[wNetRxLen] if room - ld c, a + ld c, a ; C = byte to store + ; index = wNetRxLen (16-bit); drop if >= 320 (buffer size) + ld a, [wNetRxLen+1] + or a + jr z, .instore ; high 0 -> index < 256, room + cp 1 + jr nz, .next ; high >= 2 -> overflow, drop + ld a, [wNetRxLen] + cp 64 ; high 1 -> room iff low < 64 (index < 320) + jr nc, .next +.instore ld a, [wNetRxLen] - cp 255 - jr nc, .next ; overflow: drop ld e, a - ld d, 0 + ld a, [wNetRxLen+1] + ld d, a ld hl, wNetRxBuf add hl, de ld a, c ld [hl], a - ld a, [wNetRxLen] + ld a, [wNetRxLen] ; index++ inc a ld [wNetRxLen], a + jr nz, .next + ld a, [wNetRxLen+1] + inc a + ld [wNetRxLen+1], a jr .next .frame ; END: if we have a frame, process it and return (one frame per pump, so a @@ -795,11 +827,13 @@ net_pump: xor a ld [wNetRxEsc], a ld a, [wNetRxLen] - or a + ld hl, wNetRxLen+1 + or [hl] jr z, .next ; empty frame, keep reading call process_frame xor a ld [wNetRxLen], a + ld [wNetRxLen+1], a ret ; ----------------------------------------------------------------------------- @@ -891,7 +925,9 @@ icmp_request: ld [wNetRxBuf+11], a ; send it back ld a, [wNetRxLen] - ld b, a + ld e, a + ld a, [wNetRxLen+1] + ld d, a ld hl, wNetRxBuf call net_slip_send ret @@ -1406,7 +1442,10 @@ tcp_send_seg: ld b, a ld a, [wNetTcpDlen] add b - ld b, a + ld e, a + ld a, 0 + adc 0 + ld d, a ld hl, wNetTx call net_slip_send ret diff --git a/tools/netboot b/tools/netboot index 3d27cf8..bca93ee 100755 --- a/tools/netboot +++ b/tools/netboot @@ -34,6 +34,45 @@ def slip_encode(pkt): o.append(END) return bytes(o) +# ---- built-in DHCP server: lease the GB 10.0.0.2 ---------------------------- +GB_IP = b"\x0a\x00\x00\x02"; GW = b"\x0a\x00\x00\x01" +def _cksum(d): + s = 0 + for i in range(0, len(d) - 1, 2): s += (d[i] << 8) | d[i + 1] + if len(d) % 2: s += d[-1] << 8 + while s >> 16: s = (s & 0xFFFF) + (s >> 16) + return (~s) & 0xFFFF +def _dhcp_mtype(dhcp): + p = 240 + while p < len(dhcp) and dhcp[p] != 255: + if dhcp[p] == 0: p += 1; continue + if dhcp[p] == 53: return dhcp[p + 2] + p += 2 + dhcp[p + 1] + return 0 +def _dhcp_reply(req, mt): + d = bytearray(240) + d[0] = 2; d[1] = 1; d[2] = 6 + d[4:8] = req[4:8]; d[16:20] = GB_IP; d[20:24] = GW; d[28:44] = req[28:44] + d[236:240] = b"\x63\x82\x53\x63" + d += bytes([53, 1, mt, 54, 4]) + GW + bytes([255]) + udp = bytearray(8); udp[1] = 67; udp[3] = 68 + ul = 8 + len(d); udp[4] = ul >> 8; udp[5] = ul & 0xFF + udp += d + tot = 20 + len(udp) + ip = bytearray(20); ip[0] = 0x45; ip[2] = tot >> 8; ip[3] = tot & 0xFF + ip[8] = 64; ip[9] = 17; ip[12:16] = GW; ip[16:20] = b"\xff\xff\xff\xff" + ck = _cksum(ip); ip[10] = ck >> 8; ip[11] = ck & 0xFF + return bytes(ip) + bytes(udp) +def dhcp_intercept(frame, send_raw): + if len(frame) < 28 or frame[9] != 17: return False + ihl = (frame[0] & 0x0f) * 4 + if len(frame) < ihl + 4 or ((frame[ihl + 2] << 8) | frame[ihl + 3]) != 67: return False + dhcp = frame[ihl + 8:] + mt = _dhcp_mtype(dhcp) + if mt == 1: send_raw(_dhcp_reply(dhcp, 2)) + elif mt == 3: send_raw(_dhcp_reply(dhcp, 5)) + return True + def sh(cmd): subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) @@ -99,8 +138,10 @@ def main(): for c in chunk: if c == END: if in_frame and buf: - try: os.write(tun, bytes(buf)) - except Exception: pass + fr = bytes(buf) + if not dhcp_intercept(fr, lambda pk: conn.sendall(slip_encode(pk))): + try: os.write(tun, fr) + except Exception: pass buf, in_frame, esc = bytearray(), False, False else: buf, in_frame, esc = bytearray(), True, False diff --git a/tools/tunbridge.py b/tools/tunbridge.py index f5e391c..d5669e4 100644 --- a/tools/tunbridge.py +++ b/tools/tunbridge.py @@ -27,6 +27,49 @@ def slip_encode(pkt): out.append(END) return bytes(out) +# ---- built-in DHCP server: the bridge leases the GB 10.0.0.2 ---------------- +GB_IP = b"\x0a\x00\x00\x02"; GW = b"\x0a\x00\x00\x01" +def _cksum(d): + s = 0 + for i in range(0, len(d) - 1, 2): s += (d[i] << 8) | d[i + 1] + if len(d) % 2: s += d[-1] << 8 + while s >> 16: s = (s & 0xFFFF) + (s >> 16) + return (~s) & 0xFFFF +def _dhcp_mtype(dhcp): + p = 240 + while p < len(dhcp) and dhcp[p] != 255: + if dhcp[p] == 0: p += 1; continue + if dhcp[p] == 53: return dhcp[p + 2] + p += 2 + dhcp[p + 1] + return 0 +def _dhcp_reply(req, mt): + d = bytearray(240) + d[0] = 2; d[1] = 1; d[2] = 6 + d[4:8] = req[4:8] # xid + d[16:20] = GB_IP # yiaddr + d[20:24] = GW # siaddr + d[28:44] = req[28:44] # chaddr + d[236:240] = b"\x63\x82\x53\x63" + d += bytes([53, 1, mt, 54, 4]) + GW + bytes([255]) + udp = bytearray(8); udp[1] = 67; udp[3] = 68 + ul = 8 + len(d); udp[4] = ul >> 8; udp[5] = ul & 0xFF + udp += d + tot = 20 + len(udp) + ip = bytearray(20); ip[0] = 0x45; ip[2] = tot >> 8; ip[3] = tot & 0xFF + ip[8] = 64; ip[9] = 17; ip[12:16] = GW; ip[16:20] = b"\xff\xff\xff\xff" + ck = _cksum(ip); ip[10] = ck >> 8; ip[11] = ck & 0xFF + return bytes(ip) + bytes(udp) +def dhcp_intercept(frame, send_raw): + """If frame is a DHCP request (UDP -> :67), answer it and return True.""" + if len(frame) < 28 or frame[9] != 17: return False + ihl = (frame[0] & 0x0f) * 4 + if len(frame) < ihl + 4 or ((frame[ihl + 2] << 8) | frame[ihl + 3]) != 67: return False + dhcp = frame[ihl + 8:] + mt = _dhcp_mtype(dhcp) + if mt == 1: send_raw(_dhcp_reply(dhcp, 2)) # DISCOVER -> OFFER + elif mt == 3: send_raw(_dhcp_reply(dhcp, 5)) # REQUEST -> ACK + return True + def sh(cmd, quiet=True): subprocess.run(cmd, shell=True, check=False, stdout=subprocess.DEVNULL if quiet else None, @@ -81,8 +124,10 @@ def main(): c = b[0] if c == END: if in_frame and buf: - try: os.write(tun, bytes(buf)) - except Exception: pass + fr = bytes(buf) + if not dhcp_intercept(fr, lambda pk: (p.stdin.write(slip_encode(pk)), p.stdin.flush())): + try: os.write(tun, fr) + except Exception: pass buf, in_frame, esc = bytearray(), False, False else: buf, in_frame, esc = bytearray(), True, False |
