dhcpsrv.lua 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
  130. dhcp_encode(pkg, buff)
  131. local dst = "255.255.255.255"
  132. if 4 == msgtype then
  133. dst = string.format("%d.%d.%d.%d", srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], client.ip)
  134. end
  135. -- log.info(TAG, "发送", msgtype, dst, buff:query():toHex())
  136. srv.udp:send(buff, dst, 68)
  137. end
  138. local function dhcp_send_offer(srv, pkg, client)
  139. dhcp_send_x(srv, pkg, client, 2)
  140. end
  141. local function dhcp_send_ack(srv, pkg, client)
  142. dhcp_send_x(srv, pkg, client, 5)
  143. end
  144. local function dhcp_handle_discover(srv, pkg)
  145. local mac = pkg.chaddr:sub(1, pkg.hlen)
  146. -- 看看是不是已经分配了ip
  147. for _, client in pairs(srv.clients) do
  148. if client.mac == mac then
  149. log.info(TAG, "发现已经分配的mac地址, send offer")
  150. dhcp_send_offer(srv, pkg, client)
  151. return
  152. end
  153. end
  154. -- TODO 清理已经过期的IP分配记录
  155. -- 分配一个新的ip
  156. if #srv.clients >= (srv.opts.ip_end - srv.opts.ip_start) then
  157. log.info(TAG, "没有可分配的ip了")
  158. return
  159. end
  160. local ip = nil
  161. for i = srv.opts.ip_start, srv.opts.ip_end, 1 do
  162. if srv.clients[i] == nil then
  163. ip = i
  164. break
  165. end
  166. end
  167. if ip == nil then
  168. log.info(TAG, "没有可分配的ip了")
  169. return
  170. end
  171. 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))
  172. local client = {
  173. mac = mac,
  174. ip = ip,
  175. tm = mcu.ticks() // mcu.hz(),
  176. stat = 1
  177. }
  178. srv.clients[ip] = client
  179. log.info(TAG, "send offer")
  180. dhcp_send_offer(srv, pkg, client)
  181. end
  182. local function dhcp_handle_request(srv, pkg)
  183. local mac = pkg.chaddr:sub(1, pkg.hlen)
  184. -- 看看是不是已经分配了ip
  185. for _, client in pairs(srv.clients) do
  186. if client.mac == mac then
  187. log.info(TAG, "request,发现已经分配的mac地址, send ack")
  188. client.tm = mcu.ticks() // mcu.hz()
  189. stat = 3
  190. dhcp_send_ack(srv, pkg, client)
  191. return
  192. end
  193. end
  194. end
  195. local function dhcp_pkg_handle(srv, pkg)
  196. -- 进行基本的检查
  197. if pkg.magic ~= 0x63825363 then
  198. log.warn(TAG, "dhcp数据包的magic不对劲,忽略该数据包", pkg.magic)
  199. return
  200. end
  201. if pkg.op ~= 1 then
  202. log.info(TAG, "op不对,忽略该数据包", pkg.op)
  203. return
  204. end
  205. if pkg.htype ~= 1 or pkg.hlen ~= 6 then
  206. log.warn(TAG, "htype/hlen 不认识, 忽略该数据包")
  207. return
  208. end
  209. -- 看看是不是能处理的类型, 当前只处理discover/request
  210. if pkg.msgtype == 1 or pkg.msgtype == 3 then
  211. else
  212. log.warn(TAG, "msgtype不是discover/request, 忽略该数据包", pkg.msgtype)
  213. return
  214. end
  215. -- 检查一下mac地址是否合法
  216. local mac = pkg.chaddr:sub(1, pkg.hlen)
  217. if mac == "\0\0\0\0\0\0" or mac == "\xFF\xFF\xFF\xFF\xFF\xFF" then
  218. log.warn(TAG, "mac地址为空, 忽略该数据包")
  219. return
  220. end
  221. -- 处理discover包
  222. if pkg.msgtype == 1 then
  223. log.info(TAG, "是discover包")
  224. dhcp_handle_discover(srv, pkg)
  225. elseif pkg.msgtype == 3 then
  226. log.info(TAG, "是request包")
  227. dhcp_handle_request(srv, pkg)
  228. end
  229. -- TODO 处理结束, 打印一下客户的列表?
  230. end
  231. local function dhcp_task(srv)
  232. while 1 do
  233. -- log.info("ulwip", "等待DHCP数据")
  234. local result, data = sys.waitUntil(srv.udp_topic, 1000)
  235. if result then
  236. -- log.info("ulwip", "收到dhcp数据包", data:toHex())
  237. -- 解析DHCP数据包
  238. local pkg = dhcp_decode(zbuff.create(#data, data))
  239. if pkg then
  240. -- dhcp_print_pkg(pkg)
  241. dhcp_pkg_handle(srv, pkg)
  242. end
  243. end
  244. end
  245. end
  246. function dhcpsrv.create(opts)
  247. local srv = {}
  248. if not opts then
  249. opts = {}
  250. end
  251. srv.udp_topic = "dhcpd_inc"
  252. -- 补充参数
  253. if not opts.mark then
  254. opts.mark = {255, 255, 255, 0}
  255. end
  256. if not opts.gw then
  257. opts.gw = {192, 168, 4, 1}
  258. end
  259. if not opts.dns then
  260. opts.dns = opts.gw
  261. end
  262. if not opts.ip_start then
  263. opts.ip_start = 100
  264. end
  265. if not opts.ip_end then
  266. opts.ip_end = 200
  267. end
  268. srv.clients = {}
  269. srv.opts = opts
  270. srv.udp = udpsrv.create(67, srv.udp_topic, opts.adapter)
  271. srv.task = sys.taskInit(dhcp_task, srv)
  272. return srv
  273. end
  274. return dhcpsrv