summaryrefslogtreecommitdiffstats
path: root/plugins/plugin_dhcp.uc
blob: d34a582563a1a171d8e7464437b236533324b7c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
let ubus, sub, listener, global;

const dhcp_parser_proto = {
	reset: function() {
		this.offset = 0;
	},

	parseAt: function(offset) {
		let id = hex(substr(this.buffer, offset, 2));
		let len = hex(substr(this.buffer, offset + 2, 2)) * 2;
		if (type(id) != "int" || type(len) != "int")
			return null;

		let data = substr(this.buffer, offset + 4, len);
		if (length(data) != len)
			return null;

		return [ id, data, len + 4 ];
	},

	next: function() {
		let data = this.parseAt(this.offset);
		if (!data)
			return null;

		this.offset += data[2];
		return data;
	},

	foreach: function(cb) {
		let offset = 0;
		let data;

		while ((data = this.parseAt(offset)) != null) {
			offset += data[2];
			let ret = cb(data);
			if (type(ret) == "boolean" && !ret)
				break;
		}
	},
};

function dhcp_opt_parser(data) {
	let parser = {
		offset: 0,
		buffer: data,
	};

	proto(parser, dhcp_parser_proto);

	return parser;
}

function parse_array(data)
{
	return map(match(data, /../g), (val) => val[0]);
}

function parse_string(data)
{
	return join("", map(parse_array(data[1]), (val) => chr(hex(val))));
}

function parse_macaddr(addr)
{
	return join(":", parse_array(addr));
}

function dhcp_cb(msg) {
	if (msg.type != "discover" && msg.type != "request")
		return;

	let packet = msg.data.packet;
	if (!packet)
		return;

	let opts = substr(packet, 240 * 2);
	if (length(opts) < 16)
		return;

	let macaddr = parse_macaddr(substr(packet, 28 * 2, 12));

	opts = dhcp_opt_parser(opts);
	opts.foreach((data) => {
		let id = data[0];
		switch (id) {
		case 12:
			typestr = "%device_name|dhcp_device_name";
			data = parse_string(data);
			break;
		case 55:
			typestr = "dhcp_req";
			data = join(",", map(parse_array(data[1]), (val) => hex(val)));
			break;
		case 60:
			typestr = "dhcp_vendorid";
			data = parse_string(data);
			break;
		default:
			return;
		}
		global.device_add_data(macaddr, `${typestr}|${data}`);
	});
}

function init(gl) {
	global = gl;
	ubus = gl.ubus;

	gl.weight.dhcp_req = 1.1;
	gl.weight.dhcp_device_name = 5.0;
	sub = ubus.subscriber(dhcp_cb);
	listener = ubus.listener("ubus.object.add", (event, msg) => {
		if (msg.path == "dhcpsnoop")
			sub.subscribe(msg.path);
	});
	sub.subscribe("dhcpsnoop");
}

return { init };