summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2023-02-11 22:24:31 +0100
committerMikael Magnusson <mikma@users.sourceforge.net>2023-03-27 00:36:40 +0200
commitab18e58b2462846d3a919999f416c6fc3a0e7c5e (patch)
tree33122efa3defa4e703af525553d314b357ab0210
parentb5ad0b1551498cff45ba871f8e7bdb50e303fd19 (diff)
WIP: add dhcp.go
-rw-r--r--tunnel/tools/libwg-go/dhcp.go248
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
+}