common.uc 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. import * as nl80211 from "nl80211";
  2. import * as rtnl from "rtnl";
  3. import { readfile, glob, basename, readlink } from "fs";
  4. const iftypes = {
  5. ap: nl80211.const.NL80211_IFTYPE_AP,
  6. mesh: nl80211.const.NL80211_IFTYPE_MESH_POINT,
  7. sta: nl80211.const.NL80211_IFTYPE_STATION,
  8. adhoc: nl80211.const.NL80211_IFTYPE_ADHOC,
  9. monitor: nl80211.const.NL80211_IFTYPE_MONITOR,
  10. };
  11. const mesh_params = {
  12. mesh_retry_timeout: "retry_timeout",
  13. mesh_confirm_timeout: "confirm_timeout",
  14. mesh_holding_timeout: "holding_timeout",
  15. mesh_max_peer_links: "max_peer_links",
  16. mesh_max_retries: "max_retries",
  17. mesh_ttl: "ttl",
  18. mesh_element_ttl: "element_ttl",
  19. mesh_auto_open_plinks: "auto_open_plinks",
  20. mesh_hwmp_max_preq_retries: "hwmp_max_preq_retries",
  21. mesh_path_refresh_time: "path_refresh_time",
  22. mesh_min_discovery_timeout: "min_discovery_timeout",
  23. mesh_hwmp_active_path_timeout: "hwmp_active_path_timeout",
  24. mesh_hwmp_preq_min_interval: "hwmp_preq_min_interval",
  25. mesh_hwmp_net_diameter_traversal_time: "hwmp_net_diam_trvs_time",
  26. mesh_hwmp_rootmode: "hwmp_rootmode",
  27. mesh_hwmp_rann_interval: "hwmp_rann_interval",
  28. mesh_gate_announcements: "gate_announcements",
  29. mesh_sync_offset_max_neighor: "sync_offset_max_neighbor",
  30. mesh_rssi_threshold: "rssi_threshold",
  31. mesh_hwmp_active_path_to_root_timeout: "hwmp_path_to_root_timeout",
  32. mesh_hwmp_root_interval: "hwmp_root_interval",
  33. mesh_hwmp_confirmation_interval: "hwmp_confirmation_interval",
  34. mesh_awake_window: "awake_window",
  35. mesh_plink_timeout: "plink_timeout",
  36. mesh_fwding: "forwarding",
  37. mesh_power_mode: "power_mode",
  38. mesh_nolearn: "nolearn"
  39. };
  40. function wdev_remove(name)
  41. {
  42. nl80211.request(nl80211.const.NL80211_CMD_DEL_INTERFACE, 0, { dev: name });
  43. }
  44. function __phy_is_fullmac(phyidx)
  45. {
  46. let data = nl80211.request(nl80211.const.NL80211_CMD_GET_WIPHY, 0, { wiphy: phyidx });
  47. return !data.software_iftypes.ap_vlan;
  48. }
  49. function phy_is_fullmac(phy)
  50. {
  51. let phyidx = int(trim(readfile(`/sys/class/ieee80211/${phy}/index`)));
  52. return __phy_is_fullmac(phyidx);
  53. }
  54. function find_reusable_wdev(phyidx)
  55. {
  56. if (!__phy_is_fullmac(phyidx))
  57. return null;
  58. let data = nl80211.request(
  59. nl80211.const.NL80211_CMD_GET_INTERFACE,
  60. nl80211.const.NLM_F_DUMP,
  61. { wiphy: phyidx });
  62. for (let res in data)
  63. if (trim(readfile(`/sys/class/net/${res.ifname}/operstate`)) == "down")
  64. return res.ifname;
  65. return null;
  66. }
  67. function wdev_create(phy, name, data)
  68. {
  69. let phyidx = int(readfile(`/sys/class/ieee80211/${phy}/index`));
  70. wdev_remove(name);
  71. if (!iftypes[data.mode])
  72. return `Invalid mode: ${data.mode}`;
  73. let req = {
  74. wiphy: phyidx,
  75. ifname: name,
  76. iftype: iftypes[data.mode],
  77. };
  78. if (data["4addr"])
  79. req["4addr"] = data["4addr"];
  80. if (data.macaddr)
  81. req.mac = data.macaddr;
  82. nl80211.error();
  83. let reuse_ifname = find_reusable_wdev(phyidx);
  84. if (reuse_ifname &&
  85. (reuse_ifname == name ||
  86. rtnl.request(rtnl.const.RTM_SETLINK, 0, { dev: reuse_ifname, ifname: name}) != false))
  87. nl80211.request(
  88. nl80211.const.NL80211_CMD_SET_INTERFACE, 0, {
  89. wiphy: phyidx,
  90. dev: name,
  91. iftype: iftypes[data.mode],
  92. });
  93. else
  94. nl80211.request(
  95. nl80211.const.NL80211_CMD_NEW_INTERFACE,
  96. nl80211.const.NLM_F_CREATE,
  97. req);
  98. let error = nl80211.error();
  99. if (error)
  100. return error;
  101. if (data.powersave != null) {
  102. nl80211.request(nl80211.const.NL80211_CMD_SET_POWER_SAVE, 0,
  103. { dev: name, ps_state: data.powersave ? 1 : 0});
  104. }
  105. return null;
  106. }
  107. function wdev_set_mesh_params(name, data)
  108. {
  109. let mesh_cfg = {};
  110. for (let key in mesh_params) {
  111. let val = data[key];
  112. if (val == null)
  113. continue;
  114. mesh_cfg[mesh_params[key]] = int(val);
  115. }
  116. if (!length(mesh_cfg))
  117. return null;
  118. nl80211.request(nl80211.const.NL80211_CMD_SET_MESH_CONFIG, 0,
  119. { dev: name, mesh_params: mesh_cfg });
  120. return nl80211.error();
  121. }
  122. function wdev_set_up(name, up)
  123. {
  124. rtnl.request(rtnl.const.RTM_SETLINK, 0, { dev: name, change: 1, flags: up ? 1 : 0 });
  125. }
  126. function phy_sysfs_file(phy, name)
  127. {
  128. return trim(readfile(`/sys/class/ieee80211/${phy}/${name}`));
  129. }
  130. function macaddr_split(str)
  131. {
  132. return map(split(str, ":"), (val) => hex(val));
  133. }
  134. function macaddr_join(addr)
  135. {
  136. return join(":", map(addr, (val) => sprintf("%02x", val)));
  137. }
  138. function wdev_macaddr(wdev)
  139. {
  140. return trim(readfile(`/sys/class/net/${wdev}/address`));
  141. }
  142. const phy_proto = {
  143. macaddr_init: function(used, options) {
  144. this.macaddr_options = options ?? {};
  145. this.macaddr_list = {};
  146. if (type(used) == "object")
  147. for (let addr in used)
  148. this.macaddr_list[addr] = used[addr];
  149. else
  150. for (let addr in used)
  151. this.macaddr_list[addr] = -1;
  152. this.for_each_wdev((wdev) => {
  153. let macaddr = wdev_macaddr(wdev);
  154. this.macaddr_list[macaddr] ??= -1;
  155. });
  156. return this.macaddr_list;
  157. },
  158. macaddr_generate: function(data) {
  159. let phy = this.name;
  160. let idx = int(data.id ?? 0);
  161. let mbssid = int(data.mbssid ?? 0) > 0;
  162. let num_global = int(data.num_global ?? 1);
  163. let use_global = !mbssid && idx < num_global;
  164. let base_addr = phy_sysfs_file(phy, "macaddress");
  165. if (!base_addr)
  166. return null;
  167. if (!idx && !mbssid)
  168. return base_addr;
  169. let base_mask = phy_sysfs_file(phy, "address_mask");
  170. if (!base_mask)
  171. return null;
  172. if (base_mask == "00:00:00:00:00:00" && idx >= num_global) {
  173. let addrs = split(phy_sysfs_file(phy, "addresses"), "\n");
  174. if (idx < length(addrs))
  175. return addrs[idx];
  176. base_mask = "ff:ff:ff:ff:ff:ff";
  177. }
  178. let addr = macaddr_split(base_addr);
  179. let mask = macaddr_split(base_mask);
  180. let type;
  181. if (mbssid)
  182. type = "b5";
  183. else if (use_global)
  184. type = "add";
  185. else if (mask[0] > 0)
  186. type = "b1";
  187. else if (mask[5] < 0xff)
  188. type = "b5";
  189. else
  190. type = "add";
  191. switch (type) {
  192. case "b1":
  193. if (!(addr[0] & 2))
  194. idx--;
  195. addr[0] |= 2;
  196. addr[0] ^= idx << 2;
  197. break;
  198. case "b5":
  199. if (mbssid)
  200. addr[0] |= 2;
  201. addr[5] ^= idx;
  202. break;
  203. default:
  204. for (let i = 5; i > 0; i--) {
  205. addr[i] += idx;
  206. if (addr[i] < 256)
  207. break;
  208. addr[i] %= 256;
  209. }
  210. break;
  211. }
  212. return macaddr_join(addr);
  213. },
  214. macaddr_next: function(val) {
  215. let data = this.macaddr_options ?? {};
  216. let list = this.macaddr_list;
  217. for (let i = 0; i < 32; i++) {
  218. data.id = i;
  219. let mac = this.macaddr_generate(data);
  220. if (!mac)
  221. return null;
  222. if (list[mac] != null)
  223. continue;
  224. list[mac] = val != null ? val : -1;
  225. return mac;
  226. }
  227. },
  228. for_each_wdev: function(cb) {
  229. let wdevs = glob(`/sys/class/ieee80211/${this.name}/device/net/*`);
  230. wdevs = map(wdevs, (arg) => basename(arg));
  231. for (let wdev in wdevs) {
  232. if (basename(readlink(`/sys/class/net/${wdev}/phy80211`)) != this.name)
  233. continue;
  234. cb(wdev);
  235. }
  236. }
  237. };
  238. function phy_open(phy)
  239. {
  240. let phyidx = readfile(`/sys/class/ieee80211/${phy}/index`);
  241. if (!phyidx)
  242. return null;
  243. return proto({
  244. name: phy,
  245. idx: int(phyidx)
  246. }, phy_proto);
  247. }
  248. const vlist_proto = {
  249. update: function(values, arg) {
  250. let data = this.data;
  251. let cb = this.cb;
  252. let seq = { };
  253. let new_data = {};
  254. let old_data = {};
  255. this.data = new_data;
  256. if (type(values) == "object") {
  257. for (let key in values) {
  258. old_data[key] = data[key];
  259. new_data[key] = values[key];
  260. delete data[key];
  261. }
  262. } else {
  263. for (let val in values) {
  264. let cur_key = val[0];
  265. let cur_obj = val[1];
  266. old_data[cur_key] = data[cur_key];
  267. new_data[cur_key] = val[1];
  268. delete data[cur_key];
  269. }
  270. }
  271. for (let key in data) {
  272. cb(null, data[key], arg);
  273. delete data[key];
  274. }
  275. for (let key in new_data)
  276. cb(new_data[key], old_data[key], arg);
  277. }
  278. };
  279. function is_equal(val1, val2) {
  280. let t1 = type(val1);
  281. if (t1 != type(val2))
  282. return false;
  283. if (t1 == "array") {
  284. if (length(val1) != length(val2))
  285. return false;
  286. for (let i = 0; i < length(val1); i++)
  287. if (!is_equal(val1[i], val2[i]))
  288. return false;
  289. return true;
  290. } else if (t1 == "object") {
  291. for (let key in val1)
  292. if (!is_equal(val1[key], val2[key]))
  293. return false;
  294. for (let key in val2)
  295. if (val1[key] == null)
  296. return false;
  297. return true;
  298. } else {
  299. return val1 == val2;
  300. }
  301. }
  302. function vlist_new(cb) {
  303. return proto({
  304. cb: cb,
  305. data: {}
  306. }, vlist_proto);
  307. }
  308. export { wdev_remove, wdev_create, wdev_set_mesh_params, wdev_set_up, is_equal, vlist_new, phy_is_fullmac, phy_open };