server.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package dhcp
  15. import (
  16. "net"
  17. "runtime/debug"
  18. "golang.org/x/net/bpf"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. )
  22. type DHCPServer struct {
  23. Address string
  24. Port int
  25. conn *Conn
  26. }
  27. type DHCP6Server struct {
  28. DHCPServer
  29. }
  30. // udp unicast socket
  31. func NewDHCPServer3(address string, port int) (*DHCPServer, error) {
  32. dhcpConn, err := NewSocketConn(address, port)
  33. if err != nil {
  34. return nil, errors.Wrap(err, "New DHCP connection")
  35. }
  36. return &DHCPServer{
  37. Address: address,
  38. Port: port,
  39. conn: dhcpConn,
  40. }, nil
  41. }
  42. // udp unicast socket
  43. func NewDHCP6Server3(address string, port int) (*DHCP6Server, error) {
  44. dhcpConn, err := NewSocketConn(address, port)
  45. if err != nil {
  46. return nil, errors.Wrap(err, "New DHCP6 connection")
  47. }
  48. return &DHCP6Server{
  49. DHCPServer{
  50. Address: address,
  51. Port: port,
  52. conn: dhcpConn,
  53. },
  54. }, nil
  55. }
  56. const (
  57. bpfContinue uint8 = 0
  58. bpfProcDhcpResp uint8 = 11
  59. bpfProcDhcpResp6 uint8 = 8
  60. bpfProcICMPv6 uint8 = 11
  61. bpfProcReadPkt6 uint8 = 17
  62. bpfProcEnd6 uint8 = 18
  63. bpfProcReadPkt4 uint8 = 14
  64. bpfProcEnd4 uint8 = 15
  65. udpProtocol uint32 = 17
  66. icmpProtocol uint32 = 58
  67. )
  68. var (
  69. v4bpf = []bpf.Instruction{
  70. /* 0 load ethernet type, 2bytes */
  71. &bpf.LoadAbsolute{Off: 12, Size: 2},
  72. /* 1 if ether_type != 0x0800(IPv4) then jump to [end] else continue */
  73. &bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x0800, SkipTrue: bpfGoto(2, bpfProcEnd4), SkipFalse: bpfContinue},
  74. /* 2 load ipv4 protocol, offset 23, 1 byte */
  75. &bpf.LoadAbsolute{Off: 23, Size: 1},
  76. /* 3 if ip_proto != UDP then jump to [end] else continue */
  77. &bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: udpProtocol, SkipTrue: bpfGoto(4, bpfProcEnd4), SkipFalse: bpfContinue},
  78. /* 4 if load ip flags & fragment_offset */
  79. &bpf.LoadAbsolute{Off: 20, Size: 2},
  80. /* 5 if ip_fragment_offset != 0 then jump to end/25(6+19) else continue */
  81. bpf.JumpIf{Cond: bpf.JumpBitsSet, Val: 0x1fff, SkipTrue: bpfGoto(6, bpfProcEnd4), SkipFalse: bpfContinue},
  82. /* 6 store ip_header_length*4 in register X */
  83. bpf.LoadMemShift{Off: 14},
  84. /* 7 load 14 + ip_header_len + 0 (UDP src port), 2 bytes */
  85. bpf.LoadIndirect{Off: 14, Size: 2},
  86. /* 8 if udp_src_port != 67 then jump to dhcp_resp/11(9+2) else continue */
  87. bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 67, SkipTrue: bpfGoto(9, bpfProcDhcpResp), SkipFalse: bpfContinue},
  88. /* 9 load 14 + ip_header_len + 2 (udp dst port), 2 bytes */
  89. bpf.LoadIndirect{Off: 16, Size: 2},
  90. /* 10 if udp_dst_port == 68 then jump to read/24(11+13) else jump to end/25(11+14) */
  91. bpf.JumpIf{Cond: bpf.JumpEqual, Val: 68, SkipTrue: bpfGoto(11, bpfProcReadPkt4), SkipFalse: bpfGoto(11, bpfProcEnd4)},
  92. /* 11 [dhcp_resp] if udp_src_port != 68 then jump to end/25(12+13) else continue, which means a UDP response */
  93. bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 68, SkipTrue: bpfGoto(12, bpfProcEnd4), SkipFalse: bpfContinue},
  94. /* 12 load 14 + ip_header_len + 2 (udp dst port), 2 bytes */
  95. bpf.LoadIndirect{Off: 16, Size: 2},
  96. /* 13 if udp_dst_port != 67 then jump to end/25(14+11) else continue */
  97. bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 67, SkipTrue: bpfGoto(14, bpfProcEnd4), SkipFalse: bpfContinue},
  98. /* 14 [read] return the whole packet */
  99. bpf.RetConstant{Val: 0x40000},
  100. /* 15 [end] return none */
  101. bpf.RetConstant{Val: 0x0},
  102. }
  103. v6bpf = []bpf.Instruction{
  104. /* 0 load ethernet type, 2bytes */
  105. &bpf.LoadAbsolute{Off: 12, Size: 2},
  106. /* 1 [ipv6] if ether_type != 0x86dd(IPv6) then jump to end/25(15 + 10) else continue */
  107. &bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x86dd, SkipTrue: bpfGoto(2, bpfProcEnd6), SkipFalse: bpfContinue},
  108. /* 2 load ipv6 next header, offset 20, 1 byte */
  109. &bpf.LoadAbsolute{Off: 20, Size: 1},
  110. /* 3 [udp6] if ip_proto != UDP then jump to [icmp6]/24(17 + 7) else continue */
  111. &bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: udpProtocol, SkipTrue: bpfGoto(4, bpfProcICMPv6), SkipFalse: bpfContinue},
  112. /* 4 load 14 + 40 + 0 (UDP src port), 2 bytes */
  113. bpf.LoadAbsolute{Off: 54, Size: 2},
  114. /* 5 if udp_src_port != 546 then jump to ipv6_resp/21(19+2) else continue */
  115. bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 546, SkipTrue: bpfGoto(6, bpfProcDhcpResp), SkipFalse: bpfContinue},
  116. /* 6 load 14 + 40 + 2 (udp dst port), 2 bytes */
  117. bpf.LoadAbsolute{Off: 56, Size: 2},
  118. /* 7 if udp_dst_port == 547 then jump to readPkt6 else jump to end */
  119. bpf.JumpIf{Cond: bpf.JumpEqual, Val: 547, SkipTrue: bpfGoto(8, bpfProcReadPkt6), SkipFalse: bpfGoto(8, bpfProcEnd6)},
  120. /* 8 [dhcpv6_resp] if udp_src_port != 547 then jump to end else continue, which means a DHCPv6 response */
  121. bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 547, SkipTrue: bpfGoto(9, bpfProcEnd6), SkipFalse: bpfContinue},
  122. /* 9 load 14 + 40 + 2 (udp dst port), 2 bytes */
  123. bpf.LoadAbsolute{Off: 56, Size: 2},
  124. /* 10 if udp_dst_port != 546 then jump to end else jump to readPkt6 */
  125. bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 546, SkipTrue: bpfGoto(11, bpfProcEnd6), SkipFalse: bpfGoto(11, bpfProcReadPkt6)},
  126. /* 11 [icmp6] if ip_proto != ICMP then jump to [end] else continue */
  127. &bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: icmpProtocol, SkipTrue: bpfGoto(12, bpfProcEnd6), SkipFalse: bpfContinue},
  128. /* 12 [icmp6 type] load 14 + 40 + 0 (ICMPv6 type), 1 bytes */
  129. bpf.LoadAbsolute{Off: 54, Size: 1},
  130. /* 13 [Router Solicitation] if icmp6_type == 133 then jump to [read] else [continue] */
  131. bpf.JumpIf{Cond: bpf.JumpEqual, Val: 133, SkipTrue: bpfGoto(14, bpfProcReadPkt6), SkipFalse: bpfContinue},
  132. /* 14 [Router Advertisement] if icmp6_type == 134 then jump to [read] else [continue] */
  133. bpf.JumpIf{Cond: bpf.JumpEqual, Val: 134, SkipTrue: bpfGoto(15, bpfProcReadPkt6), SkipFalse: bpfContinue},
  134. /* 15 [Neightbor Solicitation] if icmp6_type == 135 then jump to [read] else [continue] */
  135. bpf.JumpIf{Cond: bpf.JumpEqual, Val: 135, SkipTrue: bpfGoto(16, bpfProcReadPkt6), SkipFalse: bpfContinue},
  136. /* 16 [Neighbor Advertisement] if icmp6_type == 136 then jump to [read] else [end] */
  137. bpf.JumpIf{Cond: bpf.JumpEqual, Val: 136, SkipTrue: bpfGoto(17, bpfProcReadPkt6), SkipFalse: bpfGoto(17, bpfProcEnd6)},
  138. /* 17 [read] return the whole packet */
  139. bpf.RetConstant{Val: 0x40000},
  140. /* 18 [end] return none */
  141. bpf.RetConstant{Val: 0x0},
  142. }
  143. )
  144. func bpfGoto(current uint8, jumpTo uint8) uint8 {
  145. return jumpTo - current
  146. }
  147. func getRawInstructions(obpf []bpf.Instruction) ([]bpf.RawInstruction, error) {
  148. bpf := make([]bpf.RawInstruction, 0, len(obpf))
  149. for i := range obpf {
  150. inst, err := obpf[i].Assemble()
  151. if err != nil {
  152. return nil, errors.Wrap(err, "invalid eBPF instruction")
  153. }
  154. bpf = append(bpf, inst)
  155. }
  156. return bpf, nil
  157. }
  158. // raw socket
  159. func NewDHCPServer2(iface string, port uint16) (*DHCPServer, *Conn, error) {
  160. bpf, err := getRawInstructions(v4bpf)
  161. if err != nil {
  162. return nil, nil, errors.Wrap(err, "getRawInstructions")
  163. }
  164. conn, err := NewRawSocketConn(iface, bpf, port)
  165. if err != nil {
  166. return nil, nil, err
  167. }
  168. return &DHCPServer{
  169. conn: conn,
  170. }, conn, nil
  171. }
  172. func NewDHCP6Server2(iface string, port uint16) (*DHCP6Server, *Conn, error) {
  173. bpf, err := getRawInstructions(v6bpf)
  174. if err != nil {
  175. return nil, nil, errors.Wrap(err, "getRawInstructions")
  176. }
  177. conn, err := NewRawSocketConn6(iface, bpf, port)
  178. if err != nil {
  179. return nil, nil, err
  180. }
  181. return &DHCP6Server{
  182. DHCPServer{
  183. conn: conn,
  184. },
  185. }, conn, nil
  186. }
  187. func (s *DHCPServer) ListenAndServe(handler DHCPHandler) error {
  188. defer s.conn.Close()
  189. return s.serveDHCP(handler)
  190. }
  191. func (s *DHCP6Server) ListenAndServe(handler DHCP6Handler) error {
  192. defer s.conn.Close()
  193. return s.serveDHCP(handler)
  194. }
  195. func (s *DHCPServer) GetConn() *Conn {
  196. return s.conn
  197. }
  198. func (s *DHCPServer) serveDHCP(handler DHCPHandler) error {
  199. for {
  200. pkt, addr, mac, err := s.conn.RecvDHCP()
  201. if err != nil {
  202. log.Errorf("Receiving DHCP packet: %s", err)
  203. continue
  204. }
  205. go func() {
  206. defer func() {
  207. if r := recover(); r != nil {
  208. log.Errorf("Serve panic error: %v", r)
  209. debug.PrintStack()
  210. }
  211. }()
  212. resp, targets, err := handler.ServeDHCP(pkt, mac, addr)
  213. if err != nil {
  214. log.Warningf("[DHCP] handler serve error: %v", err)
  215. return
  216. }
  217. if resp == nil {
  218. // log.Warningf("[DHCP] hander response null packet")
  219. return
  220. }
  221. if len(targets) > 0 {
  222. targetUdpAddr := *addr
  223. for _, target := range targets {
  224. log.Debugf("[DHCP] Send packet back to %s", target)
  225. targetUdpAddr.IP = net.ParseIP(target)
  226. resp.SetGIAddr(targetUdpAddr.IP)
  227. if err = s.conn.SendDHCP(resp, &targetUdpAddr, mac); err != nil {
  228. log.Errorf("[DHCP] failed to response packet for %s: %v", pkt.CHAddr(), err)
  229. }
  230. }
  231. return
  232. }
  233. //log.Debugf("[DHCP] send response packet: %s to interface: %#v", resp.DebugString(), intf)
  234. if err = s.conn.SendDHCP(resp, addr, mac); err != nil {
  235. log.Errorf("[DHCP] failed to response packet for %s: %v", pkt.CHAddr(), err)
  236. return
  237. }
  238. }()
  239. }
  240. }
  241. func (s *DHCP6Server) serveDHCP(handler DHCP6Handler) error {
  242. for {
  243. pkt, addr, mac, err := s.conn.RecvDHCP6()
  244. if err != nil {
  245. log.Errorf("Receiving DHCP packet: %s", err)
  246. continue
  247. }
  248. go func() {
  249. defer func() {
  250. if r := recover(); r != nil {
  251. log.Errorf("Serve panic error: %v", r)
  252. debug.PrintStack()
  253. }
  254. }()
  255. if addr.Port == icmpRAFakePort {
  256. // receive a RA solication
  257. handler.OnRecvICMP6(pkt)
  258. return
  259. }
  260. resp, targets, err := handler.ServeDHCP(pkt, mac, addr)
  261. if err != nil {
  262. log.Warningf("[DHCP6] handler serve error: %s", err)
  263. return
  264. }
  265. if resp == nil {
  266. log.Warningf("[DHCP6] hander response null packet")
  267. return
  268. }
  269. if len(targets) > 0 {
  270. targetUdpAddr := *addr
  271. for _, target := range targets {
  272. log.Debugf("[DHCP6] Send packet back to %s", target)
  273. targetUdpAddr.IP = net.ParseIP(target)
  274. if err = s.conn.SendDHCP6(resp, &targetUdpAddr, mac); err != nil {
  275. log.Errorf("[DHCP6] failed to response packet for %s: %v", pkt.CHAddr(), err)
  276. }
  277. }
  278. return
  279. }
  280. //log.Debugf("[DHCP] send response packet: %s to interface: %#v", resp.DebugString(), intf)
  281. if err = s.conn.SendDHCP6(resp, addr, mac); err != nil {
  282. log.Errorf("[DHCP6] failed to response packet for %s: %v", pkt.CHAddr(), err)
  283. return
  284. }
  285. }()
  286. }
  287. }