dhcpsrv.lua 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. local dhcpsrv = {}
  2. local udpsrv = require("udpsrv")
  3. local TAG = "dhcpsrv"
  4. ----
  5. -- 参考地址
  6. -- https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol
  7. local function dhcp_decode(buff)
  8. -- buff:seek(0)
  9. local dst = {}
  10. -- 开始解析dhcp
  11. dst.op = buff[0]
  12. dst.htype = buff[1]
  13. dst.hlen = buff[2]
  14. dst.hops = buff[3]
  15. buff:seek(4)
  16. dst.xid = buff:read(4)
  17. _, dst.secs = buff:unpack(">H")
  18. _, dst.flags = buff:unpack(">H")
  19. dst.ciaddr = buff:read(4)
  20. dst.yiaddr = buff:read(4)
  21. dst.siaddr = buff:read(4)
  22. dst.giaddr = buff:read(4)
  23. dst.chaddr = buff:read(16)
  24. -- 跳过192字节
  25. buff:seek(192, zbuff.SEEK_CUR)
  26. -- 解析magic
  27. _, dst.magic = buff:unpack(">I")
  28. -- 解析option
  29. local opt = {}
  30. while buff:len() > buff:used() do
  31. local tag = buff:read(1):byte()
  32. if tag ~= 0 then
  33. local len = buff:read(1):byte()
  34. if tag == 0xFF or len == 0 then
  35. break
  36. end
  37. local data = buff:read(len)
  38. if tag == 53 then
  39. -- 53: DHCP Message Type
  40. dst.msgtype = data:byte()
  41. end
  42. table.insert(opt, {tag, data})
  43. -- log.info(TAG, "tag", tag, "data", data:toHex())
  44. end
  45. end
  46. if dst.msgtype == nil then
  47. return -- 没有解析到msgtype,直接返回
  48. end
  49. dst.opts = opt
  50. return dst
  51. end
  52. local function dhcp_buff2ip(buff)
  53. return string.format("%d.%d.%d.%d", buff:byte(1), buff:byte(2), buff:byte(3), buff:byte(4))
  54. end
  55. local function dhcp_print_pkg(pkg)
  56. log.info(TAG, "XID", pkg.xid:toHex())
  57. log.info(TAG, "secs", pkg.secs)
  58. log.info(TAG, "flags", pkg.flags)
  59. log.info(TAG, "chaddr", pkg.chaddr:sub(1, pkg.hlen):toHex())
  60. log.info(TAG, "yiaddr", dhcp_buff2ip(pkg.yiaddr))
  61. log.info(TAG, "siaddr", dhcp_buff2ip(pkg.siaddr))
  62. log.info(TAG, "giaddr", dhcp_buff2ip(pkg.giaddr))
  63. log.info(TAG, "ciaddr", dhcp_buff2ip(pkg.ciaddr))
  64. log.info(TAG, "magic", string.format("%08X", pkg.magic))
  65. for _, opt in pairs(pkg.opts) do
  66. if opt[1] == 53 then
  67. log.info(TAG, "msgtype", opt[2]:byte())
  68. elseif opt[1] == 60 then
  69. log.info(TAG, "auth", opt[2])
  70. elseif opt[1] == 57 then
  71. log.info(TAG, "Maximum DHCP message size", opt[2]:byte() * 256 + opt[2]:byte(2))
  72. elseif opt[1] == 61 then
  73. log.info(TAG, "Client-identifier", opt[2]:toHex())
  74. elseif opt[1] == 55 then
  75. log.info(TAG, "Parameter request list", opt[2]:toHex())
  76. elseif opt[1] == 12 then
  77. log.info(TAG, "Host name", opt[2])
  78. -- elseif opt[1] == 58 then
  79. -- log.info(TAG, "Renewal (T1) time value", opt[2]:unpack(">I"))
  80. end
  81. end
  82. end
  83. local function dhcp_encode(pkg, buff)
  84. -- 合成DHCP包
  85. buff:seek(0)
  86. buff[0] = pkg.op
  87. buff[1] = pkg.htype
  88. buff[2] = pkg.hlen
  89. buff[3] = pkg.hops
  90. buff:seek(4)
  91. -- 写入XID
  92. buff:write(pkg.xid)
  93. -- 几个重要的参数
  94. buff:pack(">H", pkg.secs)
  95. buff:pack(">H", pkg.flags)
  96. buff:write(pkg.ciaddr)
  97. buff:write(pkg.yiaddr)
  98. buff:write(pkg.siaddr)
  99. buff:write(pkg.giaddr)
  100. -- 写入MAC地址
  101. buff:write(pkg.chaddr)
  102. -- 跳过192字节
  103. buff:seek(192, zbuff.SEEK_CUR)
  104. -- 写入magic
  105. buff:pack(">I", pkg.magic)
  106. -- 写入option
  107. for _, opt in pairs(pkg.opts) do
  108. buff:write(opt[1])
  109. buff:write(#opt[2])
  110. buff:write(opt[2])
  111. end
  112. buff:write(0xFF, 0x00)
  113. end
  114. ----
  115. local function dhcp_send_x(srv, pkg, client, msgtype)
  116. local buff = zbuff.create(300)
  117. pkg.op = 2
  118. pkg.ciaddr = "\0\0\0\0"
  119. pkg.yiaddr = string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], client.ip)
  120. pkg.siaddr = string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])
  121. pkg.giaddr = "\0\0\0\0"
  122. pkg.secs = 0
  123. pkg.opts = {} -- 复位option
  124. table.insert(pkg.opts, {53, string.char(msgtype)})
  125. table.insert(pkg.opts, {1, string.char(srv.opts.mark[1], srv.opts.mark[2], srv.opts.mark[3], srv.opts.mark[4])})
  126. table.insert(pkg.opts, {3, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
  127. table.insert(pkg.opts, {51, "\x00\x00\x1E\x00"}) -- 7200秒, 大概
  128. table.insert(pkg.opts, {54, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
  129. table.insert(pkg.opts, {6, string.char(223, 5, 5, 5)})
  130. table.insert(pkg.opts, {6, string.char(119, 29, 29, 29)})
  131. table.insert(pkg.opts, {6, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
  132. dhcp_encode(pkg, buff)
  133. local dst = "255.255.255.255"
  134. if 4 == msgtype then
  135. dst = string.format("%d.%d.%d.%d", srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], client.ip)
  136. end
  137. -- log.info(TAG, "发送", msgtype, dst, buff:query():toHex())
  138. srv.udp:send(buff, dst, 68)
  139. end
  140. local function dhcp_send_offer(srv, pkg, client)
  141. dhcp_send_x(srv, pkg, client, 2)
  142. end
  143. local function dhcp_send_ack(srv, pkg, client)
  144. dhcp_send_x(srv, pkg, client, 5)
  145. end
  146. local function dhcp_send_nack(srv, pkg, client)
  147. dhcp_send_x(srv, pkg, client, 6)
  148. end
  149. local function dhcp_handle_discover(srv, pkg)
  150. local mac = pkg.chaddr:sub(1, pkg.hlen)
  151. -- 看看是不是已经分配了ip
  152. for _, client in pairs(srv.clients) do
  153. if client.mac == mac then
  154. log.info(TAG, "发现已经分配的mac地址, send offer")
  155. dhcp_send_offer(srv, pkg, client)
  156. return
  157. end
  158. end
  159. -- TODO 清理已经过期的IP分配记录
  160. -- 分配一个新的ip
  161. if #srv.clients >= (srv.opts.ip_end - srv.opts.ip_start) then
  162. log.info(TAG, "没有可分配的ip了")
  163. return
  164. end
  165. local ip = nil
  166. for i = srv.opts.ip_start, srv.opts.ip_end, 1 do
  167. if srv.clients[i] == nil then
  168. ip = i
  169. break
  170. end
  171. end
  172. if ip == nil then
  173. log.info(TAG, "没有可分配的ip了")
  174. return
  175. end
  176. log.info(TAG, "分配ip", mac:toHex(), string.format("%d.%d.%d.%d", srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], ip))
  177. local client = {
  178. mac = mac,
  179. ip = ip,
  180. tm = mcu.ticks() // mcu.hz(),
  181. stat = 1
  182. }
  183. srv.clients[ip] = client
  184. log.info(TAG, "send offer")
  185. dhcp_send_offer(srv, pkg, client)
  186. end
  187. local function dhcp_handle_request(srv, pkg)
  188. local mac = pkg.chaddr:sub(1, pkg.hlen)
  189. -- 看看是不是已经分配了ip
  190. for _, client in pairs(srv.clients) do
  191. if client.mac == mac then
  192. log.info(TAG, "request,发现已经分配的mac地址, send ack")
  193. client.tm = mcu.ticks() // mcu.hz()
  194. stat = 3
  195. dhcp_send_ack(srv, pkg, client)
  196. return
  197. end
  198. end
  199. -- 没有找到, 那应该返回NACK
  200. log.info(TAG, "request,没有分配的mac地址, send nack")
  201. dhcp_send_nack(srv, pkg, {ip=pkg.yiaddr:byte(1)})
  202. end
  203. local function dhcp_pkg_handle(srv, pkg)
  204. -- 进行基本的检查
  205. if pkg.magic ~= 0x63825363 then
  206. log.warn(TAG, "dhcp数据包的magic不对劲,忽略该数据包", pkg.magic)
  207. return
  208. end
  209. if pkg.op ~= 1 then
  210. log.info(TAG, "op不对,忽略该数据包", pkg.op)
  211. return
  212. end
  213. if pkg.htype ~= 1 or pkg.hlen ~= 6 then
  214. log.warn(TAG, "htype/hlen 不认识, 忽略该数据包")
  215. return
  216. end
  217. -- 看看是不是能处理的类型, 当前只处理discover/request
  218. if pkg.msgtype == 1 or pkg.msgtype == 3 then
  219. else
  220. log.warn(TAG, "msgtype不是discover/request, 忽略该数据包", pkg.msgtype)
  221. return
  222. end
  223. -- 检查一下mac地址是否合法
  224. local mac = pkg.chaddr:sub(1, pkg.hlen)
  225. if mac == "\0\0\0\0\0\0" or mac == "\xFF\xFF\xFF\xFF\xFF\xFF" then
  226. log.warn(TAG, "mac地址为空, 忽略该数据包")
  227. return
  228. end
  229. -- 处理discover包
  230. if pkg.msgtype == 1 then
  231. log.info(TAG, "是discover包")
  232. dhcp_handle_discover(srv, pkg)
  233. elseif pkg.msgtype == 3 then
  234. log.info(TAG, "是request包")
  235. dhcp_handle_request(srv, pkg)
  236. end
  237. -- TODO 处理结束, 打印一下客户的列表?
  238. end
  239. local function dhcp_task(srv)
  240. while 1 do
  241. -- log.info("ulwip", "等待DHCP数据")
  242. local result, data = sys.waitUntil(srv.udp_topic, 1000)
  243. if result then
  244. -- log.info("ulwip", "收到dhcp数据包", data:toHex())
  245. -- 解析DHCP数据包
  246. local pkg = dhcp_decode(zbuff.create(#data, data))
  247. if pkg then
  248. -- dhcp_print_pkg(pkg)
  249. dhcp_pkg_handle(srv, pkg)
  250. end
  251. end
  252. end
  253. end
  254. function dhcpsrv.create(opts)
  255. local srv = {}
  256. if not opts then
  257. opts = {}
  258. end
  259. srv.udp_topic = "dhcpd_inc"
  260. -- 补充参数
  261. if not opts.mark then
  262. opts.mark = {255, 255, 255, 0}
  263. end
  264. if not opts.gw then
  265. opts.gw = {192, 168, 4, 1}
  266. end
  267. if not opts.dns then
  268. opts.dns = opts.gw
  269. end
  270. if not opts.ip_start then
  271. opts.ip_start = 100
  272. end
  273. if not opts.ip_end then
  274. opts.ip_end = 200
  275. end
  276. srv.clients = {}
  277. srv.opts = opts
  278. srv.udp = udpsrv.create(67, srv.udp_topic, opts.adapter)
  279. srv.task = sys.taskInit(dhcp_task, srv)
  280. return srv
  281. end
  282. return dhcpsrv