golang DHCPv4/v6 demo

server端代码如下:

package main

import (
        "errors"
        "fmt"
        "github.com/insomniacslk/dhcp/dhcpv4"
        "github.com/insomniacslk/dhcp/dhcpv4/server4"
        "github.com/insomniacslk/dhcp/dhcpv6"
        "github.com/insomniacslk/dhcp/dhcpv6/server6"
        "github.com/insomniacslk/dhcp/iana"
        "github.com/mdlayher/ndp"
        "golang.org/x/net/ipv6"
        "log"
        "net"
        "time"
)

type dhcpV4 struct{}
type dhcpV6 struct{}

func (v4 *dhcpV4) sendOffer(conn net.PacketConn, m *dhcpv4.DHCPv4, peer net.Addr) {
        requestIpAddr := m.RequestedIPAddress()
        if requestIpAddr == nil {
                requestIpAddr = net.IP{192, 168, 0, 100}
        }

        offer, err := dhcpv4.New(
                dhcpv4.WithMessageType(dhcpv4.MessageTypeOffer),
                dhcpv4.WithServerIP(net.IP{192, 168, 0, 1}),
                dhcpv4.WithYourIP(requestIpAddr),
                dhcpv4.WithHwAddr(m.ClientHWAddr),
        )
        if err != nil {
                log.Fatal(err)
                return
        }
        offer.OpCode = dhcpv4.OpcodeBootReply
        offer.ServerHostName = "test"
        offer.TransactionID = m.TransactionID

        offer.UpdateOption(dhcpv4.OptSubnetMask(net.IPMask{255, 255, 255, 0}))
        offer.UpdateOption(dhcpv4.OptRouter(net.IPv4(192, 168, 0, 1)))
        offer.UpdateOption(dhcpv4.OptServerIdentifier(net.IP{192, 168, 0, 2}))

        if _, err := conn.WriteTo(offer.ToBytes(), peer); err != nil {
                log.Fatal(err)
        }
        log.Print(offer.Summary())
}

func (v4 *dhcpV4) sendReplyFromRequest(conn net.PacketConn, m *dhcpv4.DHCPv4, peer net.Addr) {
        requestIpAddr := m.RequestedIPAddress()
        if requestIpAddr == nil {
                requestIpAddr = net.IP{192, 168, 0, 100}
        }

        ack, err := dhcpv4.New(
                dhcpv4.WithMessageType(dhcpv4.MessageTypeAck),
                dhcpv4.WithServerIP(net.IP{192, 168, 0, 1}),
                dhcpv4.WithYourIP(requestIpAddr),
                dhcpv4.WithHwAddr(m.ClientHWAddr),
        )
        if err != nil {
                log.Fatal(err)
                return
        }

        ack.OpCode = dhcpv4.OpcodeBootReply
        ack.ServerHostName = "test"
        ack.TransactionID = m.TransactionID

        ack.UpdateOption(dhcpv4.OptSubnetMask(net.IPMask{255, 255, 255, 0}))
        ack.UpdateOption(dhcpv4.OptRouter(net.IPv4(192, 168, 0, 1)))
        ack.UpdateOption(dhcpv4.OptServerIdentifier(net.IP{192, 168, 0, 2}))
        ack.UpdateOption(dhcpv4.OptDNS(net.IPv4(8, 8, 8, 8), net.IPv4(114, 114, 114, 114)))
        ack.UpdateOption(dhcpv4.OptIPAddressLeaseTime(43200 * time.Second))

        // static route
        _, ipNet1, err := net.ParseCIDR("1.1.1.0/24")
        if err != nil {
                panic(err)
                return
        }
        route1 := &dhcpv4.Route{
                Dest:   ipNet1,
                Router: net.IP{192, 168, 0, 220},
        }

        _, ipNet2, err := net.ParseCIDR("2.2.2.0/24")
        if err != nil {
                panic(err)
                return
        }
        route2 := &dhcpv4.Route{
                Dest:   ipNet2,
                Router: net.IP{192, 168, 0, 221},
        }

        _, ipNet3, err := net.ParseCIDR("0.0.0.0/0")
        if err != nil {
                panic(err)
                return
        }
        route3 := &dhcpv4.Route{
                Dest:   ipNet3,
                Router: net.IP{192, 168, 0, 1},
        }
        ack.UpdateOption(dhcpv4.OptClasslessStaticRoute(route1, route2, route3))

        if _, err := conn.WriteTo(ack.ToBytes(), peer); err != nil {
                log.Fatal(err)
        }
        log.Print(ack.Summary())
}

func (v4 *dhcpV4) sendReplyFromRelease(conn net.PacketConn, m *dhcpv4.DHCPv4, peer net.Addr) {

}

func (v6 *dhcpV6) sendAdvertise(conn net.PacketConn, m dhcpv6.DHCPv6, peer net.Addr) {
        message, err := m.GetInnerMessage()
        if err != nil {
                log.Fatal(err)
                return
        }

        iface, err := net.InterfaceByName(IFNAME)
        if err != nil {
                log.Fatal(err)
        }
        sid := dhcpv6.Duid{
                Type:          dhcpv6.DUID_LLT,
                HwType:        iana.HWTypeEthernet,
                Time:          dhcpv6.GetTime(),
                LinkLayerAddr: iface.HardwareAddr,
        }

        adv, err := dhcpv6.NewAdvertiseFromSolicit(message, dhcpv6.WithServerID(sid))
        if err != nil {
                log.Fatal(err)
        }

        // add Elapsed Time
        adv.AddOption(dhcpv6.OptElapsedTime(0))

        // add IA_NA
        {
                addr := &dhcpv6.OptIAAddress{
                        IPv6Addr:          net.ParseIP("4000::100"),
                        PreferredLifetime: 0x1C20 * time.Second,
                        ValidLifetime:     0x2A30 * time.Second,
                }

                //addrPrefix := &dhcpv6.OptIAPrefix{
                //      Prefix: &net.IPNet{
                //              Mask: net.CIDRMask(64, 128),
                //              IP:   net.ParseIP("4000::1"),
                //      },
                //      PreferredLifetime: 0x1C20 * time.Second,
                //      ValidLifetime:     0x2A30 * time.Second,
                //}

                iana := message.Options.OneIANA()
                iana.T1 = time.Duration(3600) * time.Second
                iana.T2 = time.Duration(5400) * time.Second

                iana.Options.Add(addr)
                //iana.Options.Add(addrPrefix)

                adv.AddOption(iana)
        }

        // add status code
        {
                opt := dhcpv6.OptStatusCode{
                        StatusCode:    iana.StatusSuccess,
                        StatusMessage: "success",
                }
                adv.AddOption(&opt)
        }

        // add dns opt
        {
                ns1 := net.ParseIP("1000:2000:1000::1")
                ns2 := net.ParseIP("1000:2000:1000::2")
                nameservers := []net.IP{ns1, ns2}
                opt := dhcpv6.OptDNS(nameservers...)
                adv.AddOption(opt)
        }

        // add domain search list
        {
        }

        if _, err := conn.WriteTo(adv.ToBytes(), peer); err != nil {
                log.Fatal(err)
        }
        log.Print(adv.Summary())
}

func (v6 *dhcpV6) sendReplyFromRequest(conn net.PacketConn, request dhcpv6.DHCPv6, peer net.Addr) {
        message, err := request.GetInnerMessage()
        if err != nil {
                log.Fatal(err)
        }

        // add Server ID
        iface, err := net.InterfaceByName(IFNAME)
        if err != nil {
                log.Fatal(err)
        }
        sid := dhcpv6.Duid{
                Type:          dhcpv6.DUID_LLT,
                HwType:        iana.HWTypeEthernet,
                Time:          dhcpv6.GetTime(),
                LinkLayerAddr: iface.HardwareAddr,
        }

        rep, err := dhcpv6.NewReplyFromMessage(message, dhcpv6.WithServerID(sid))
        if err != nil {
                log.Fatal(err)
        }

        // add IA_NA
        {
                iana := message.Options.OneIANA()
                iana.T1 = time.Duration(3600) * time.Second
                iana.T2 = time.Duration(5400) * time.Second
                rep.AddOption(iana)
        }

        // add status code
        {
                opt := dhcpv6.OptStatusCode{
                        StatusCode:    iana.StatusSuccess,
                        StatusMessage: "success",
                }
                rep.AddOption(&opt)
        }

        // dns
        {
                ns1 := net.ParseIP("1000:2000:1000::1")
                ns2 := net.ParseIP("1000:2000:1000::2")
                nameservers := []net.IP{ns1, ns2}
                opt := dhcpv6.OptDNS(nameservers...)
                rep.AddOption(opt)
        }

        // IAPD
        //oAddr := dhcpv6.OptIAPrefix{
        //      Prefix: &net.IPNet{
        //              Mask: net.CIDRMask(64, 128),
        //              IP:   net.ParseIP("4000::100"),
        //      },
        //      PreferredLifetime: 0x1C20 * time.Second,
        //      ValidLifetime:     0x2A30 * time.Second,
        //}
        //opt := dhcpv6.OptIAPD{
        //      IaId:    [4]byte{175, 62, 198, 235},
        //      T1:      time.Duration(3600) * time.Second,
        //      T2:      time.Duration(5400) * time.Second,
        //      Options: dhcpv6.PDOptions{[]dhcpv6.Option{&oAddr}},
        //}
        //rep.AddOption(&opt)

        if _, err := conn.WriteTo(rep.ToBytes(), peer); err != nil {
                log.Fatal(err)
        }
        log.Print(rep.Summary())
}

func (v6 *dhcpV6) sendReplyFromConfirm(conn net.PacketConn, confirm dhcpv6.DHCPv6, peer net.Addr) {
        message, err := confirm.GetInnerMessage()
        if err != nil {
                log.Fatal(err)
        }

        // add Server ID
        iface, err := net.InterfaceByName(IFNAME)
        if err != nil {
                log.Fatal(err)
        }
        sid := dhcpv6.Duid{
                Type:          dhcpv6.DUID_LLT,
                HwType:        iana.HWTypeEthernet,
                Time:          dhcpv6.GetTime(),
                LinkLayerAddr: iface.HardwareAddr,
        }

        rep, err := dhcpv6.NewReplyFromMessage(message, dhcpv6.WithServerID(sid))
        if err != nil {
                log.Fatal(err)
        }

        // add IA_NA
        {
                iana := message.Options.OneIANA()
                iana.T1 = time.Duration(3600) * time.Second
                iana.T2 = time.Duration(5400) * time.Second
                rep.AddOption(iana)
        }

        // add status code
        {
                opt := dhcpv6.OptStatusCode{
                        StatusCode:    iana.StatusSuccess,
                        StatusMessage: "success",
                }
                rep.AddOption(&opt)
        }

        // dns
        {
                ns1 := net.ParseIP("1000:2000:1000::1")
                ns2 := net.ParseIP("1000:2000:1000::2")
                nameservers := []net.IP{ns1, ns2}
                opt := dhcpv6.OptDNS(nameservers...)
                rep.AddOption(opt)
        }

        if _, err := conn.WriteTo(rep.ToBytes(), peer); err != nil {
                log.Fatal(err)
        }
        log.Print(rep.Summary())
}

func (v6 *dhcpV6) sendReplyFromRebind(conn net.PacketConn, rebind dhcpv6.DHCPv6, peer net.Addr) {
        message, err := rebind.GetInnerMessage()
        if err != nil {
                log.Fatal(err)
        }

        // add Server ID
        iface, err := net.InterfaceByName(IFNAME)
        if err != nil {
                log.Fatal(err)
        }
        sid := dhcpv6.Duid{
                Type:          dhcpv6.DUID_LLT,
                HwType:        iana.HWTypeEthernet,
                Time:          dhcpv6.GetTime(),
                LinkLayerAddr: iface.HardwareAddr,
        }

        rep, err := dhcpv6.NewReplyFromMessage(message, dhcpv6.WithServerID(sid))
        if err != nil {
                log.Fatal(err)
        }

        // add IA_NA
        {
                iana := message.Options.OneIANA()
                iana.T1 = time.Duration(3600) * time.Second
                iana.T2 = time.Duration(5400) * time.Second
                rep.AddOption(iana)
        }

        // add status code
        {
                opt := dhcpv6.OptStatusCode{
                        StatusCode:    iana.StatusSuccess,
                        StatusMessage: "success",
                }
                rep.AddOption(&opt)
        }

        // dns
        {
                ns1 := net.ParseIP("1000:2000:1000::1")
                ns2 := net.ParseIP("1000:2000:1000::2")
                nameservers := []net.IP{ns1, ns2}
                opt := dhcpv6.OptDNS(nameservers...)
                rep.AddOption(opt)
        }

        if _, err := conn.WriteTo(rep.ToBytes(), peer); err != nil {
                log.Fatal(err)
        }
        log.Print(rep.Summary())
}

func (v6 *dhcpV6) sendReplyFromRelease(conn net.PacketConn, release dhcpv6.DHCPv6, peer net.Addr) {
        message, err := release.GetInnerMessage()
        if err != nil {
                log.Fatal(err)
        }

        // add Server ID
        iface, err := net.InterfaceByName(IFNAME)
        if err != nil {
                log.Fatal(err)
        }
        sid := dhcpv6.Duid{
                Type:          dhcpv6.DUID_LLT,
                HwType:        iana.HWTypeEthernet,
                Time:          dhcpv6.GetTime(),
                LinkLayerAddr: iface.HardwareAddr,
        }

        rep, err := dhcpv6.NewReplyFromMessage(message, dhcpv6.WithServerID(sid))
        if err != nil {
                log.Fatal(err)
        }

        // add status code
        opt := dhcpv6.OptStatusCode{
                StatusCode:    iana.StatusSuccess,
                StatusMessage: "success",
        }
        rep.AddOption(&opt)

        if _, err := conn.WriteTo(rep.ToBytes(), peer); err != nil {
                log.Fatal(err)
        }
        log.Print(rep.Summary())
}

func handlerV4(conn net.PacketConn, peer net.Addr, m *dhcpv4.DHCPv4) {
        dhcpType := m.MessageType()
        log.Printf("type: %+v.", dhcpType)
        log.Print(m.Summary())
        v4 := dhcpV4{}

        switch dhcpType {
        case dhcpv4.MessageTypeDiscover:
                v4.sendOffer(conn, m, peer)
        case dhcpv4.MessageTypeRequest:
                v4.sendReplyFromRequest(conn, m, peer)
        case dhcpv4.MessageTypeInform:
        case dhcpv4.MessageTypeRelease:
                v4.sendReplyFromRelease(conn, m, peer)
        }
}

func handlerV6(conn net.PacketConn, peer net.Addr, m dhcpv6.DHCPv6) {
        dhcpType := m.Type()
        log.Printf("type: %+v.", dhcpType)
        log.Print(m.Summary())
        v6 := dhcpV6{}

        switch dhcpType {
        case dhcpv6.MessageTypeSolicit:
                v6.sendAdvertise(conn, m, peer)
        case dhcpv6.MessageTypeRequest:
                v6.sendReplyFromRequest(conn, m, peer)
        case dhcpv6.MessageTypeConfirm:
                v6.sendReplyFromConfirm(conn, m, peer)
        case dhcpv6.MessageTypeRebind:
                v6.sendReplyFromRebind(conn, m, peer)
        case dhcpv6.MessageTypeRelease:
                v6.sendReplyFromRelease(conn, m, peer)
        case dhcpv6.MessageTypeDecline:
        case dhcpv6.MessageTypeReconfigure:
        case dhcpv6.MessageTypeInformationRequest:
        case dhcpv6.MessageTypeRenew:
        case dhcpv6.MessageTypeRelayForward:
        }
}

func handleRouterSolicitation(c *ndp.Conn, msg ndp.Message, ifname string) {
        log.Printf("start handle icmpv6 packet, type is %+v.", msg.Type())

        // fetch source mac
        intf, err := net.InterfaceByName(ifname)
        if err != nil {
                log.Fatal(err)
                return
        }
        addr := intf.HardwareAddr

        raMsg := &ndp.RouterAdvertisement{
                CurrentHopLimit:           64,
                ManagedConfiguration:      true,
                OtherConfiguration:        true,
                RouterSelectionPreference: ndp.Medium,
                RouterLifetime:            ndp.Infinity,
                ReachableTime:             0,
                RetransmitTimer:           0,
                Options: []ndp.Option{
                        &ndp.LinkLayerAddress{
                                Direction: ndp.Source,
                                Addr:      addr,
                        },
                        ndp.NewMTU(1500),
                        &ndp.PrefixInformation{
                                PrefixLength:                   96,
                                OnLink:                         true,
                                AutonomousAddressConfiguration: true,
                                ValidLifetime:                  ndp.Infinity,
                                PreferredLifetime:              ndp.Infinity,
                                Prefix:                         net.ParseIP("4000::"),
                        },
                        &ndp.RouteInformation{
                                PrefixLength:  0,
                                Preference:    ndp.Medium,
                                RouteLifetime: ndp.Infinity,
                                Prefix:        net.IPv6zero,
                        },
                        &ndp.RouteInformation{
                                PrefixLength:  64,
                                Preference:    ndp.Medium,
                                RouteLifetime: ndp.Infinity,
                                Prefix:        net.ParseIP("6000::"),
                        },
                        &ndp.RouteInformation{
                                PrefixLength:  128,
                                Preference:    ndp.Medium,
                                RouteLifetime: ndp.Infinity,
                                Prefix:        net.ParseIP("8000::1"),
                        },
                },
        }

        if err := c.WriteTo(raMsg, nil, net.IPv6linklocalallnodes); err != nil {
                log.Printf("failed to send router advertisement: %v", err)
                return
        }

        log.Printf("end of handle send route advertisement packet.")
}

func findInterface(name string) (*net.Interface, error) {
        if name != "" {
                ifi, err := net.InterfaceByName(name)
                if err != nil {
                        return nil, fmt.Errorf("could not find interface %q: %v", name, err)
                }

                return ifi, nil
        }

        ifis, err := net.Interfaces()
        if err != nil {
                return nil, err
        }

        for _, ifi := range ifis {
                // Is the interface up and not a loopback?
                if ifi.Flags&net.FlagUp != 1 || ifi.Flags&net.FlagLoopback != 0 {
                        continue
                }

                // Does the interface have an IPv6 address assigned?
                addrs, err := ifi.Addrs()
                if err != nil {
                        return nil, err
                }

                for _, a := range addrs {
                        ipNet, ok := a.(*net.IPNet)
                        if !ok {
                                continue
                        }

                        // Is this address an IPv6 address?
                        if ipNet.IP.To16() != nil && ipNet.IP.To4() == nil {
                                return &ifi, nil
                        }
                }
        }

        return nil, errors.New("could not find a usable IPv6-enabled interface")
}

func listenPacket(ifname string, addrFlag string) {
        ifi, err := findInterface(ifname)
        if err != nil {
                log.Fatalf("failed to get interface: %v", err)
                return
        }

        addr := ndp.Addr(addrFlag)
        c, _, err := ndp.Dial(ifi, addr)
        if err != nil {
                log.Fatalf("failed to dial NDP connection: %v", err)
                return
        }
        defer c.Close()

        if err := c.JoinGroup(net.IPv6linklocalallrouters); err != nil {
                log.Fatal(err)
                return
        }
        if err := c.JoinGroup(net.IPv6linklocalallnodes); err != nil {
                log.Fatal(err)
                return
        }

        for {
                m, _, _, err := c.ReadFrom()
                if err != nil {
                        log.Printf("ReadFrom failed: %v", err)
                        return
                }
                fmt.Println(m, m.Type())

                msgType := m.Type()
                switch msgType {
                case ipv6.ICMPTypeRouterSolicitation:
                        go handleRouterSolicitation(c, m, ifname)
                default:
                        continue
                }
        }
}

var (
        IFNAME string = "ens38"
        DEVMAC string = "fe80::20c:29ff:fe10:dc1a"
)

func main() {
        log.Printf("dhcpv4 server start ... ...")
        laddrV4 := &net.UDPAddr{
                IP:   net.ParseIP("0.0.0.0"),
                Port: 67,
        }
        serverV4, err := server4.NewServer(IFNAME, laddrV4, handlerV4, server4.WithDebugLogger())
        if err != nil {
                log.Fatal(err)
        }
        go serverV4.Serve()

        log.Printf("dhcpv6 server start ... ...")
        laddrV6 := &net.UDPAddr{
                IP:   net.ParseIP("::"),
                Port: 547,
        }
        serverV6, err := server6.NewServer(IFNAME, laddrV6, handlerV6, server6.WithDebugLogger())
        if err != nil {
                log.Fatal(err)
        }

        go serverV6.Serve()

        listenPacket(IFNAME, DEVMAC)
}
原文地址:https://www.cnblogs.com/wangjq19920210/p/14182776.html