add dnsmasq and example config for it
would be good to move more of this into a module, but that doesn't sit well with the (potential) ability to run more than one dnsmasq service, as modules are singletons
This commit is contained in:
parent
6f23a45696
commit
c320d0afc7
7 changed files with 175 additions and 3 deletions
|
@ -1,6 +1,6 @@
|
|||
{ config, pkgs, ... } :
|
||||
{ config, pkgs, lib, ... } :
|
||||
let
|
||||
inherit (pkgs.liminix.networking) interface address pppoe route;
|
||||
inherit (pkgs.liminix.networking) interface address pppoe route dnsmasq;
|
||||
inherit (pkgs.liminix.services) oneshot longrun bundle target output;
|
||||
in rec {
|
||||
services.loopback =
|
||||
|
@ -13,6 +13,10 @@ in rec {
|
|||
];
|
||||
};
|
||||
|
||||
services.lan4 =
|
||||
let iface = interface { type = "hardware"; device = "eth1";};
|
||||
in address iface { family = "inet4"; address ="192.168.19.1"; prefixLength = 24;};
|
||||
|
||||
kernel.config = {
|
||||
"IKCONFIG_PROC" = "y";
|
||||
"PPP" = "y";
|
||||
|
@ -53,14 +57,29 @@ in rec {
|
|||
dependencies = [iface];
|
||||
};
|
||||
|
||||
users.dnsmasq = {
|
||||
uid = 51; gid= 51; gecos = "DNS/DHCP service user";
|
||||
dir = "/run/dnsmasq";
|
||||
shell = "/bin/false";
|
||||
};
|
||||
groups.dnsmasq = {
|
||||
gid = 51; usernames = ["dnsmasq"];
|
||||
};
|
||||
services.dns =
|
||||
dnsmasq {
|
||||
interface = services.lan4;
|
||||
ranges = ["192.168.19.10,192.168.19.253"];
|
||||
domain = "fake.liminix.org";
|
||||
};
|
||||
|
||||
services.default = target {
|
||||
name = "default";
|
||||
contents = with services; [
|
||||
loopback
|
||||
defaultroute4
|
||||
packet_forwarding
|
||||
dns
|
||||
];
|
||||
};
|
||||
|
||||
defaultProfile.packages = [ pkgs.hello ] ;
|
||||
}
|
||||
|
|
|
@ -23,3 +23,9 @@ fi
|
|||
|
||||
../../scripts/run-qemu.sh --background foo.sock result/vmlinux result/squashfs
|
||||
nix-shell -p expect --run "expect getaddress.expect"
|
||||
|
||||
set -o pipefail
|
||||
response=$(nix-shell -p python3Packages.scapy --run 'python ./test-dhcp-service.py' )
|
||||
|
||||
echo "$response"
|
||||
echo "$response" | nix-shell -p jq --run "jq -e 'select((.router == \"192.168.19.1\") and (.server_id==\"192.168.19.1\"))'"
|
||||
|
|
88
tests/pppoe/test-dhcp-service.py
Normal file
88
tests/pppoe/test-dhcp-service.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
# forge packets for testing liminix and send them via the qemu udp
|
||||
# multicast socket interface
|
||||
|
||||
MCAST_GRP = '230.0.0.1'
|
||||
MCAST_PORT = 1235
|
||||
MULTICAST_TTL = 2
|
||||
|
||||
TIMEOUT = 10 # seconds
|
||||
|
||||
from warnings import filterwarnings
|
||||
filterwarnings("ignore")
|
||||
|
||||
import random
|
||||
import binascii
|
||||
import socket
|
||||
import time
|
||||
import json
|
||||
|
||||
from builtins import bytes, bytearray
|
||||
|
||||
from scapy.all import Ether, IP, UDP, BOOTP, DHCP, sendp, send, raw
|
||||
|
||||
class JSONEncoderWithBytes(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, (bytes, bytearray)):
|
||||
return obj.decode('utf-8')
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
||||
|
||||
def dhcp_option(pkt, label):
|
||||
if pkt.haslayer(DHCP):
|
||||
for i in pkt[DHCP].options:
|
||||
l, v = i
|
||||
if l == label:
|
||||
return v
|
||||
return None
|
||||
|
||||
def is_dhcp_offer(pkt):
|
||||
val = dhcp_option(pkt, 'message-type')
|
||||
return (val == 2)
|
||||
|
||||
|
||||
|
||||
def mac_to_bytes(mac_addr: str) -> bytes:
|
||||
""" Converts a MAC address string to bytes.
|
||||
"""
|
||||
return int(mac_addr.replace(":", ""), 16).to_bytes(6, "big")
|
||||
|
||||
|
||||
client_mac = "01:02:03:04:05:06"
|
||||
discover = (
|
||||
Ether(dst="ff:ff:ff:ff:ff:ff") /
|
||||
IP(src="0.0.0.0", dst="255.255.255.255") /
|
||||
UDP(sport=68, dport=67) /
|
||||
BOOTP(
|
||||
chaddr=mac_to_bytes(client_mac),
|
||||
xid=random.randint(1, 2**32-1),
|
||||
) /
|
||||
DHCP(options=[("message-type", "discover"), "end"])
|
||||
)
|
||||
payload = raw(discover)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
sock.settimeout(TIMEOUT)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
|
||||
|
||||
sock.bind((MCAST_GRP, MCAST_PORT))
|
||||
host = socket.gethostbyname(socket.gethostname())
|
||||
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(host))
|
||||
sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP,
|
||||
socket.inet_aton(MCAST_GRP) + socket.inet_aton(host))
|
||||
|
||||
endtime = time.time() + TIMEOUT
|
||||
sock.sendto(payload, (MCAST_GRP, MCAST_PORT))
|
||||
|
||||
while time.time() < endtime:
|
||||
try:
|
||||
data, addr = sock.recvfrom(1024)
|
||||
except socket.error:
|
||||
print('Exception')
|
||||
else:
|
||||
reply = Ether(data)
|
||||
if is_dhcp_offer(reply):
|
||||
opts = dict([o for o in reply[DHCP].options if type(o) is tuple])
|
||||
print(json.dumps(opts, cls=JSONEncoderWithBytes))
|
||||
exit(0)
|
||||
exit(1)
|
Loading…
Add table
Add a link
Reference in a new issue