aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-17 14:11:22 +0200
committeruser <user@clank>2026-07-17 14:11:22 +0200
commita2cb756693229c7880d926d2cdde2f838c697415 (patch)
treee327278e7d8b7150d7a2b01cf38b4895caab7356 /tools
parentkernel: sys_sleep - a cooperative delay off the DIV timer (+ ping pacing) (diff)
downloadgbos-a2cb756693229c7880d926d2cdde2f838c697415.tar.gz
gbos-a2cb756693229c7880d926d2cdde2f838c697415.tar.xz
gbos-a2cb756693229c7880d926d2cdde2f838c697415.zip
net: DHCP client - lease the IP at boot instead of hardcoding it
The address is no longer baked in. net_init starts at 0.0.0.0; a DHCP client runs as the first thing on boot (init/shell forks it and waits), and only once it has a lease (or gives up) does the prompt appear. Kernel: - IP is configurable: 0.0.0.0 until leased; NET_SETIP op stores it. - 16-bit frame length. DHCP/BOOTP packets are ~272 bytes, over the old 255-byte frame cap, so net_slip_send takes a 16-bit length, net_pump assembles into a 320-byte buffer with a 16-bit wNetRxLen, and udp_send writes a 16-bit IP total. Payloads stay <=255 (kept small on purpose) so the per-protocol datalen math is unchanged. wNetTx/wNetRxBuf 256->320, SK_RXBUF 208->288. Userland: - c/dhcp.c: DISCOVER->OFFER->REQUEST->ACK over a UDP socket, then net_setip(); times out gracefully (shell still boots) if there's no server. sh.c runs it before the prompt. Bridge (self-contained DHCP server, no dnsmasq): - tunbridge.py + netboot intercept UDP->:67 and answer OFFER/ACK leasing 10.0.0.2 (gateway 10.0.0.1); everything else is bridged/NATed as before. Regression-tested ICMP/UDP/TCP after the 16-bit change. Verified end to end: dhcp: discovering dhcp: leased 10.0.0.2 /# ping 10.0.0.1 -> 4/4 (traffic from the leased address)
Diffstat (limited to '')
-rwxr-xr-xtools/netboot45
-rw-r--r--tools/tunbridge.py49
2 files changed, 90 insertions, 4 deletions
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