diff options
Diffstat (limited to 'tunnel/tools/libwg-go/dhcp.go')
-rw-r--r-- | tunnel/tools/libwg-go/dhcp.go | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/tunnel/tools/libwg-go/dhcp.go b/tunnel/tools/libwg-go/dhcp.go new file mode 100644 index 00000000..70a95f27 --- /dev/null +++ b/tunnel/tools/libwg-go/dhcp.go @@ -0,0 +1,248 @@ +package main + +import ( + "context" + "fmt" + "log" + "net" + "net/netip" + + "github.com/insomniacslk/dhcp/dhcpv4" + "github.com/insomniacslk/dhcp/dhcpv6" + "github.com/insomniacslk/dhcp/dhcpv6/nclient6" + "github.com/insomniacslk/dhcp/iana" +) + +const ( + ENABLE_PD = false + //ENABLE_PD = true + ENABLE_TA = false + //ENABLE_TA = true + //ENABLE_4O6 = false + ENABLE_4O6 = true +) + +func withDHCPv4Msg(msg *dhcpv4.DHCPv4) dhcpv6.Modifier { + return func(d dhcpv6.DHCPv6) { + opt := dhcpv6.OptDHCPv4Msg{ + Msg: msg, + } + d.UpdateOption(&opt) + } +} + +func newDHCPv4Query(flags uint32, modifiers ...dhcpv6.Modifier) (*dhcpv6.Message, error) { + msg, err := dhcpv6.NewMessage() + if err != nil { + return nil, err + } + msg.MessageType = dhcpv6.MessageTypeDHCPv4Query + msg.TransactionID = dhcpv6.TransactionID{byte(flags >> 16), byte(flags >> 8), byte(flags)} + //msg.AddOption(dhcpv6.OptElapsedTime(0)) + + for _, mod := range modifiers { + mod(msg) + } + return msg, nil +} + +// type DHCPv4o6Client struct { +// nclient4.Client +// } + +func dhcpv4Query(ctx context.Context, client *nclient6.Client, dest *net.UDPAddr, msg *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, error) { + query, err := newDHCPv4Query(0x800000, withDHCPv4Msg(msg)) + if err != nil { + return nil, err + } + + resp, err := client.SendAndRead(ctx, dest, query, nil) + if err != nil { + return nil, err + } + + msgOpt := resp.GetOneOption(dhcpv6.OptionDHCPv4Msg) + if msgOpt == nil { + return nil, fmt.Errorf("Missing DHCPv4Msg option in response") + } + + offer := msgOpt.(*dhcpv6.OptDHCPv4Msg).Msg + return offer, nil +} + +func newClientIDOpt(duid *dhcpv6.Duid) dhcpv4.Option { + iaid := []byte{0, 0, 0, 3} + ident := []byte{255} // Type IAID+DUID + ident = append(ident, iaid...) // IAID + ident = append(ident, duid.ToBytes()...) // DUID + return dhcpv4.OptClientIdentifier(ident) +} + +func runDhcp4o6(ctx context.Context, client *nclient6.Client, v4o6 *dhcpv6.OptDHCP4oDHCP6Server, duid *dhcpv6.Duid, hwAddr net.HardwareAddr, hostName string) (*netip.Prefix, error) { + serverAddr := nclient6.AllDHCPRelayAgentsAndServers + if len(v4o6.DHCP4oDHCP6Servers) > 0 { + // TODO use all servers + serverAddr = &net.UDPAddr{ + IP: v4o6.DHCP4oDHCP6Servers[0], + Port: dhcpv6.DefaultServerPort, + } + } + + modHostName := dhcpv4.WithGeneric(dhcpv4.OptionHostName, []byte(hostName)) + disc, err := dhcpv4.NewDiscovery(hwAddr, dhcpv4.WithOption(newClientIDOpt(duid)), modHostName) + if err != nil { + return nil, err + } + offer, err := dhcpv4Query(ctx, client, serverAddr, disc) + if err != nil { + return nil, err + } + req, err := dhcpv4.NewRequestFromOffer(offer, modHostName) + if err != nil { + return nil, err + } + ack, err := dhcpv4Query(ctx, client, serverAddr, req) + if err != nil { + return nil, err + } + + // client.Close() + fmt.Println("doClient end", ack.YourIPAddr, ack.SubnetMask()) + if ip4 := ack.YourIPAddr.To4(); ip4 == nil { + return nil, fmt.Errorf("Not IPv4! %v", ack.YourIPAddr) + } else { + log.Println("IPv4", ip4) + } + + addr, _ := netip.AddrFromSlice(ack.YourIPAddr) + prefix := netip.PrefixFrom(addr, 32) // TODO use SubnetMask + log.Printf("Append %v", prefix) + return &prefix, nil +} + +func RunDhcp(ctx context.Context, conn *net.UDPConn, hwAddr net.HardwareAddr) ([]netip.Prefix, error) { + client, err := nclient6.NewWithConn(conn, hwAddr, nclient6.WithDebugLogger()) + if err != nil { + return nil, err + } + + duid := dhcpv6.Duid{ + Type: dhcpv6.DUID_LL, + HwType: iana.HWTypeEthernet, + LinkLayerAddr: hwAddr, + } + + hostName := "gvisor" + fqdn := hostName + ".m7n.se" + fqdnOpt := dhcpv6.WithFQDN(0x1, fqdn) + + solicitMods := []dhcpv6.Modifier{} + + if ENABLE_PD { + _, iaIPNet, err := net.ParseCIDR("::/64") + if err != nil { + return nil, err + } + iaPrefix := dhcpv6.OptIAPrefix{ + 0, 0, iaIPNet, dhcpv6.PrefixOptions{}, + } + solicitMods = append(solicitMods, dhcpv6.WithIAPD([4]byte{0, 0, 0, 1}, &iaPrefix)) + } + + if ENABLE_TA { + solicitMods = append(solicitMods, dhcpv6.WithIATA([4]byte{0, 0, 0, 1})) + } + + oro := dhcpv6.WithRequestedOptions(dhcpv6.OptionDHCP4oDHCP6Server, dhcpv6.OptionFQDN) + + solicitMods = append(solicitMods, dhcpv6.WithIAID([4]byte{0, 0, 0, 1})) + solicitMods = append(solicitMods, fqdnOpt) + solicitMods = append(solicitMods, dhcpv6.WithClientID(duid)) + solicitMods = append(solicitMods, oro) + + adv, err := client.Solicit(ctx, solicitMods...) + if err != nil { + return nil, err + } + + requestMods := []dhcpv6.Modifier{} + requestMods = append(requestMods, fqdnOpt) + requestMods = append(requestMods, oro) + + if ENABLE_TA { + // Add IA_TA from advertise which is not added by default. + withIaTa := func(req dhcpv6.DHCPv6) { + if iaTa := adv.GetOneOption(dhcpv6.OptionIATA); iaTa != nil { + req.AddOption(iaTa) + } + } + requestMods = append(requestMods, withIaTa) + } + + msg, err := client.Request(ctx, adv, requestMods...) + if err != nil { + return nil, err + } + + iana := msg.GetOneOption(dhcpv6.OptionIANA).(*dhcpv6.OptIANA) + ianaOpts := iana.Options.Get(dhcpv6.OptionIAAddr) + + var iapdOpts []dhcpv6.Option + + if ENABLE_PD { + iapd := msg.GetOneOption(dhcpv6.OptionIAPD).(*dhcpv6.OptIAPD) + iapdOpts = iapd.Options.Get(dhcpv6.OptionIAPrefix) + } + + addrs := make([]netip.Prefix, 0, len(ianaOpts)+len(iapdOpts)+1) + + opt4o6 := msg.GetOneOption(dhcpv6.OptionDHCP4oDHCP6Server) + if ENABLE_4O6 && opt4o6 != nil { + v4o6 := opt4o6.(*dhcpv6.OptDHCP4oDHCP6Server) + log.Println("dhcp4o6: ", v4o6) + + prefix, err := runDhcp4o6(ctx, client, v4o6, &duid, hwAddr, hostName) + if err == nil { + addrs = append(addrs, *prefix) + } else { + return nil, err + } + } + + for _, addr := range ianaOpts { + prefix := netip.PrefixFrom(netip.IPv6Unspecified(), 128) + ip, ok := netip.AddrFromSlice(addr.(*dhcpv6.OptIAAddress).IPv6Addr) + if ok { + prefix = netip.PrefixFrom(ip, 128) + } + + log.Printf("IANA %v", prefix) + addrs = append(addrs, prefix) + } + + for _, addr := range iapdOpts { + ipnet := addr.(*dhcpv6.OptIAPrefix).Prefix + addr, _ := netip.AddrFromSlice(ipnet.IP) // TODO use ok + prefixlen, _ := ipnet.Mask.Size() + prefix := netip.PrefixFrom(addr, prefixlen) + + log.Printf("IAPD %v", prefix) + addrs = append(addrs, prefix) + } + + opt := msg.GetOneOption(dhcpv6.OptionIATA) + if opt != nil { + iata := opt.(*dhcpv6.OptIATA) + taOpts := iata.Options.Get(dhcpv6.OptionIAAddr) + + for _, addr := range taOpts { + ip := addr.(*dhcpv6.OptIAAddress).IPv6Addr + addr, _ := netip.AddrFromSlice(ip) // TODO use ok + prefix := netip.PrefixFrom(addr, 128) + + log.Printf("IATA %v", prefix) + } + } + + return addrs, nil +} |