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) }