Initial import
[project/ufp.git] / files / usr / share / ufp / plugin_dhcp.uc
1 let ubus, sub, listener, global;
2
3 const dhcp_parser_proto = {
4 reset: function() {
5 this.offset = 0;
6 },
7
8 parseAt: function(offset) {
9 let id = hex(substr(this.buffer, offset, 2));
10 let len = hex(substr(this.buffer, offset + 2, 2)) * 2;
11 if (type(id) != "int" || type(len) != "int")
12 return null;
13
14 let data = substr(this.buffer, offset + 4, len);
15 if (length(data) != len)
16 return null;
17
18 return [ id, data, len + 4 ];
19 },
20
21 next: function() {
22 let data = this.parseAt(this.offset);
23 if (!data)
24 return null;
25
26 this.offset += data[2];
27 return data;
28 },
29
30 foreach: function(cb) {
31 let offset = 0;
32 let data;
33
34 while ((data = this.parseAt(offset)) != null) {
35 offset += data[2];
36 let ret = cb(data);
37 if (type(ret) == "boolean" && !ret)
38 break;
39 }
40 },
41 };
42
43 function dhcp_opt_parser(data) {
44 let parser = {
45 offset: 0,
46 buffer: data,
47 };
48
49 proto(parser, dhcp_parser_proto);
50
51 return parser;
52 }
53
54 function parse_array(data)
55 {
56 return map(match(data, /../g), (val) => val[0]);
57 }
58
59 function parse_string(data)
60 {
61 return join("", map(parse_array(data[1]), (val) => chr(hex(val))));
62 }
63
64 function parse_macaddr(addr)
65 {
66 return join(":", parse_array(addr));
67 }
68
69 function dhcp_cb(msg) {
70 if (msg.type != "discover" && msg.type != "request")
71 return;
72
73 let packet = msg.data.packet;
74 if (!packet)
75 return;
76
77 let opts = substr(packet, 240 * 2);
78 if (length(opts) < 16)
79 return;
80
81 let macaddr = parse_macaddr(substr(packet, 28 * 2, 12));
82
83 opts = dhcp_opt_parser(opts);
84 opts.foreach((data) => {
85 let id = data[0];
86 switch (id) {
87 case 12:
88 typestr = "%device_name|dhcp_device_name";
89 data = parse_string(data);
90 break;
91 case 55:
92 typestr = "dhcp_req";
93 data = join(",", map(parse_array(data[1]), (val) => hex(val)));
94 break;
95 case 60:
96 typestr = "dhcp_vendorid";
97 data = parse_string(data);
98 break;
99 default:
100 return;
101 }
102 global.device_add_data(macaddr, `${typestr}|${data}`);
103 });
104 }
105
106 function init(gl) {
107 global = gl;
108 ubus = gl.ubus;
109
110 gl.weight.dhcp_req = 1.1;
111 gl.weight.dhcp_device_name = 5.0;
112 sub = ubus.subscriber(dhcp_cb);
113 listener = ubus.listener("ubus.object.add", (event, msg) => {
114 if (msg.path == "dhcpsnoop")
115 sub.subscribe(msg.path);
116 });
117 sub.subscribe("dhcpsnoop");
118 }
119
120 return { init };