| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package dhcp
- import (
- "net"
- "runtime/debug"
- "golang.org/x/net/bpf"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- )
- type DHCPServer struct {
- Address string
- Port int
- conn *Conn
- }
- type DHCP6Server struct {
- DHCPServer
- }
- // udp unicast socket
- func NewDHCPServer3(address string, port int) (*DHCPServer, error) {
- dhcpConn, err := NewSocketConn(address, port)
- if err != nil {
- return nil, errors.Wrap(err, "New DHCP connection")
- }
- return &DHCPServer{
- Address: address,
- Port: port,
- conn: dhcpConn,
- }, nil
- }
- // udp unicast socket
- func NewDHCP6Server3(address string, port int) (*DHCP6Server, error) {
- dhcpConn, err := NewSocketConn(address, port)
- if err != nil {
- return nil, errors.Wrap(err, "New DHCP6 connection")
- }
- return &DHCP6Server{
- DHCPServer{
- Address: address,
- Port: port,
- conn: dhcpConn,
- },
- }, nil
- }
- const (
- bpfContinue uint8 = 0
- bpfProcDhcpResp uint8 = 11
- bpfProcDhcpResp6 uint8 = 8
- bpfProcICMPv6 uint8 = 11
- bpfProcReadPkt6 uint8 = 17
- bpfProcEnd6 uint8 = 18
- bpfProcReadPkt4 uint8 = 14
- bpfProcEnd4 uint8 = 15
- udpProtocol uint32 = 17
- icmpProtocol uint32 = 58
- )
- var (
- v4bpf = []bpf.Instruction{
- /* 0 load ethernet type, 2bytes */
- &bpf.LoadAbsolute{Off: 12, Size: 2},
- /* 1 if ether_type != 0x0800(IPv4) then jump to [end] else continue */
- &bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x0800, SkipTrue: bpfGoto(2, bpfProcEnd4), SkipFalse: bpfContinue},
- /* 2 load ipv4 protocol, offset 23, 1 byte */
- &bpf.LoadAbsolute{Off: 23, Size: 1},
- /* 3 if ip_proto != UDP then jump to [end] else continue */
- &bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: udpProtocol, SkipTrue: bpfGoto(4, bpfProcEnd4), SkipFalse: bpfContinue},
- /* 4 if load ip flags & fragment_offset */
- &bpf.LoadAbsolute{Off: 20, Size: 2},
- /* 5 if ip_fragment_offset != 0 then jump to end/25(6+19) else continue */
- bpf.JumpIf{Cond: bpf.JumpBitsSet, Val: 0x1fff, SkipTrue: bpfGoto(6, bpfProcEnd4), SkipFalse: bpfContinue},
- /* 6 store ip_header_length*4 in register X */
- bpf.LoadMemShift{Off: 14},
- /* 7 load 14 + ip_header_len + 0 (UDP src port), 2 bytes */
- bpf.LoadIndirect{Off: 14, Size: 2},
- /* 8 if udp_src_port != 67 then jump to dhcp_resp/11(9+2) else continue */
- bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 67, SkipTrue: bpfGoto(9, bpfProcDhcpResp), SkipFalse: bpfContinue},
- /* 9 load 14 + ip_header_len + 2 (udp dst port), 2 bytes */
- bpf.LoadIndirect{Off: 16, Size: 2},
- /* 10 if udp_dst_port == 68 then jump to read/24(11+13) else jump to end/25(11+14) */
- bpf.JumpIf{Cond: bpf.JumpEqual, Val: 68, SkipTrue: bpfGoto(11, bpfProcReadPkt4), SkipFalse: bpfGoto(11, bpfProcEnd4)},
- /* 11 [dhcp_resp] if udp_src_port != 68 then jump to end/25(12+13) else continue, which means a UDP response */
- bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 68, SkipTrue: bpfGoto(12, bpfProcEnd4), SkipFalse: bpfContinue},
- /* 12 load 14 + ip_header_len + 2 (udp dst port), 2 bytes */
- bpf.LoadIndirect{Off: 16, Size: 2},
- /* 13 if udp_dst_port != 67 then jump to end/25(14+11) else continue */
- bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 67, SkipTrue: bpfGoto(14, bpfProcEnd4), SkipFalse: bpfContinue},
- /* 14 [read] return the whole packet */
- bpf.RetConstant{Val: 0x40000},
- /* 15 [end] return none */
- bpf.RetConstant{Val: 0x0},
- }
- v6bpf = []bpf.Instruction{
- /* 0 load ethernet type, 2bytes */
- &bpf.LoadAbsolute{Off: 12, Size: 2},
- /* 1 [ipv6] if ether_type != 0x86dd(IPv6) then jump to end/25(15 + 10) else continue */
- &bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x86dd, SkipTrue: bpfGoto(2, bpfProcEnd6), SkipFalse: bpfContinue},
- /* 2 load ipv6 next header, offset 20, 1 byte */
- &bpf.LoadAbsolute{Off: 20, Size: 1},
- /* 3 [udp6] if ip_proto != UDP then jump to [icmp6]/24(17 + 7) else continue */
- &bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: udpProtocol, SkipTrue: bpfGoto(4, bpfProcICMPv6), SkipFalse: bpfContinue},
- /* 4 load 14 + 40 + 0 (UDP src port), 2 bytes */
- bpf.LoadAbsolute{Off: 54, Size: 2},
- /* 5 if udp_src_port != 546 then jump to ipv6_resp/21(19+2) else continue */
- bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 546, SkipTrue: bpfGoto(6, bpfProcDhcpResp), SkipFalse: bpfContinue},
- /* 6 load 14 + 40 + 2 (udp dst port), 2 bytes */
- bpf.LoadAbsolute{Off: 56, Size: 2},
- /* 7 if udp_dst_port == 547 then jump to readPkt6 else jump to end */
- bpf.JumpIf{Cond: bpf.JumpEqual, Val: 547, SkipTrue: bpfGoto(8, bpfProcReadPkt6), SkipFalse: bpfGoto(8, bpfProcEnd6)},
- /* 8 [dhcpv6_resp] if udp_src_port != 547 then jump to end else continue, which means a DHCPv6 response */
- bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 547, SkipTrue: bpfGoto(9, bpfProcEnd6), SkipFalse: bpfContinue},
- /* 9 load 14 + 40 + 2 (udp dst port), 2 bytes */
- bpf.LoadAbsolute{Off: 56, Size: 2},
- /* 10 if udp_dst_port != 546 then jump to end else jump to readPkt6 */
- bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 546, SkipTrue: bpfGoto(11, bpfProcEnd6), SkipFalse: bpfGoto(11, bpfProcReadPkt6)},
- /* 11 [icmp6] if ip_proto != ICMP then jump to [end] else continue */
- &bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: icmpProtocol, SkipTrue: bpfGoto(12, bpfProcEnd6), SkipFalse: bpfContinue},
- /* 12 [icmp6 type] load 14 + 40 + 0 (ICMPv6 type), 1 bytes */
- bpf.LoadAbsolute{Off: 54, Size: 1},
- /* 13 [Router Solicitation] if icmp6_type == 133 then jump to [read] else [continue] */
- bpf.JumpIf{Cond: bpf.JumpEqual, Val: 133, SkipTrue: bpfGoto(14, bpfProcReadPkt6), SkipFalse: bpfContinue},
- /* 14 [Router Advertisement] if icmp6_type == 134 then jump to [read] else [continue] */
- bpf.JumpIf{Cond: bpf.JumpEqual, Val: 134, SkipTrue: bpfGoto(15, bpfProcReadPkt6), SkipFalse: bpfContinue},
- /* 15 [Neightbor Solicitation] if icmp6_type == 135 then jump to [read] else [continue] */
- bpf.JumpIf{Cond: bpf.JumpEqual, Val: 135, SkipTrue: bpfGoto(16, bpfProcReadPkt6), SkipFalse: bpfContinue},
- /* 16 [Neighbor Advertisement] if icmp6_type == 136 then jump to [read] else [end] */
- bpf.JumpIf{Cond: bpf.JumpEqual, Val: 136, SkipTrue: bpfGoto(17, bpfProcReadPkt6), SkipFalse: bpfGoto(17, bpfProcEnd6)},
- /* 17 [read] return the whole packet */
- bpf.RetConstant{Val: 0x40000},
- /* 18 [end] return none */
- bpf.RetConstant{Val: 0x0},
- }
- )
- func bpfGoto(current uint8, jumpTo uint8) uint8 {
- return jumpTo - current
- }
- func getRawInstructions(obpf []bpf.Instruction) ([]bpf.RawInstruction, error) {
- bpf := make([]bpf.RawInstruction, 0, len(obpf))
- for i := range obpf {
- inst, err := obpf[i].Assemble()
- if err != nil {
- return nil, errors.Wrap(err, "invalid eBPF instruction")
- }
- bpf = append(bpf, inst)
- }
- return bpf, nil
- }
- // raw socket
- func NewDHCPServer2(iface string, port uint16) (*DHCPServer, *Conn, error) {
- bpf, err := getRawInstructions(v4bpf)
- if err != nil {
- return nil, nil, errors.Wrap(err, "getRawInstructions")
- }
- conn, err := NewRawSocketConn(iface, bpf, port)
- if err != nil {
- return nil, nil, err
- }
- return &DHCPServer{
- conn: conn,
- }, conn, nil
- }
- func NewDHCP6Server2(iface string, port uint16) (*DHCP6Server, *Conn, error) {
- bpf, err := getRawInstructions(v6bpf)
- if err != nil {
- return nil, nil, errors.Wrap(err, "getRawInstructions")
- }
- conn, err := NewRawSocketConn6(iface, bpf, port)
- if err != nil {
- return nil, nil, err
- }
- return &DHCP6Server{
- DHCPServer{
- conn: conn,
- },
- }, conn, nil
- }
- func (s *DHCPServer) ListenAndServe(handler DHCPHandler) error {
- defer s.conn.Close()
- return s.serveDHCP(handler)
- }
- func (s *DHCP6Server) ListenAndServe(handler DHCP6Handler) error {
- defer s.conn.Close()
- return s.serveDHCP(handler)
- }
- func (s *DHCPServer) GetConn() *Conn {
- return s.conn
- }
- func (s *DHCPServer) serveDHCP(handler DHCPHandler) error {
- for {
- pkt, addr, mac, err := s.conn.RecvDHCP()
- if err != nil {
- log.Errorf("Receiving DHCP packet: %s", err)
- continue
- }
- go func() {
- defer func() {
- if r := recover(); r != nil {
- log.Errorf("Serve panic error: %v", r)
- debug.PrintStack()
- }
- }()
- resp, targets, err := handler.ServeDHCP(pkt, mac, addr)
- if err != nil {
- log.Warningf("[DHCP] handler serve error: %v", err)
- return
- }
- if resp == nil {
- // log.Warningf("[DHCP] hander response null packet")
- return
- }
- if len(targets) > 0 {
- targetUdpAddr := *addr
- for _, target := range targets {
- log.Debugf("[DHCP] Send packet back to %s", target)
- targetUdpAddr.IP = net.ParseIP(target)
- resp.SetGIAddr(targetUdpAddr.IP)
- if err = s.conn.SendDHCP(resp, &targetUdpAddr, mac); err != nil {
- log.Errorf("[DHCP] failed to response packet for %s: %v", pkt.CHAddr(), err)
- }
- }
- return
- }
- //log.Debugf("[DHCP] send response packet: %s to interface: %#v", resp.DebugString(), intf)
- if err = s.conn.SendDHCP(resp, addr, mac); err != nil {
- log.Errorf("[DHCP] failed to response packet for %s: %v", pkt.CHAddr(), err)
- return
- }
- }()
- }
- }
- func (s *DHCP6Server) serveDHCP(handler DHCP6Handler) error {
- for {
- pkt, addr, mac, err := s.conn.RecvDHCP6()
- if err != nil {
- log.Errorf("Receiving DHCP packet: %s", err)
- continue
- }
- go func() {
- defer func() {
- if r := recover(); r != nil {
- log.Errorf("Serve panic error: %v", r)
- debug.PrintStack()
- }
- }()
- if addr.Port == icmpRAFakePort {
- // receive a RA solication
- handler.OnRecvICMP6(pkt)
- return
- }
- resp, targets, err := handler.ServeDHCP(pkt, mac, addr)
- if err != nil {
- log.Warningf("[DHCP6] handler serve error: %s", err)
- return
- }
- if resp == nil {
- log.Warningf("[DHCP6] hander response null packet")
- return
- }
- if len(targets) > 0 {
- targetUdpAddr := *addr
- for _, target := range targets {
- log.Debugf("[DHCP6] Send packet back to %s", target)
- targetUdpAddr.IP = net.ParseIP(target)
- if err = s.conn.SendDHCP6(resp, &targetUdpAddr, mac); err != nil {
- log.Errorf("[DHCP6] failed to response packet for %s: %v", pkt.CHAddr(), err)
- }
- }
- return
- }
- //log.Debugf("[DHCP] send response packet: %s to interface: %#v", resp.DebugString(), intf)
- if err = s.conn.SendDHCP6(resp, addr, mac); err != nil {
- log.Errorf("[DHCP6] failed to response packet for %s: %v", pkt.CHAddr(), err)
- return
- }
- }()
- }
- }
|