From 80d8a71da7249766be583e8dd64d3cd566ab86c5 Mon Sep 17 00:00:00 2001 From: Anatole Denis Date: Sun, 3 Nov 2019 12:01:31 +0100 Subject: dhcpv6: Add all current option codes from IANA This updates the option codes list in dhcpv6/types.go to include all published option codes as of today. For better formatting of the table, replace comments indicating undefined options with assignments to the throwaway variable `_`, which avoids resetting alignment formatting compared to the comment option. Signed-off-by: Anatole Denis --- dhcpv6/types.go | 339 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 237 insertions(+), 102 deletions(-) diff --git a/dhcpv6/types.go b/dhcpv6/types.go index 4285465..8fd4381 100644 --- a/dhcpv6/types.go +++ b/dhcpv6/types.go @@ -81,41 +81,41 @@ func (o OptionCode) String() string { // All DHCPv6 options. const ( - OptionClientID OptionCode = 1 - OptionServerID OptionCode = 2 - OptionIANA OptionCode = 3 - OptionIATA OptionCode = 4 - OptionIAAddr OptionCode = 5 - OptionORO OptionCode = 6 - OptionPreference OptionCode = 7 - OptionElapsedTime OptionCode = 8 - OptionRelayMsg OptionCode = 9 - // skip 10 - OptionAuth OptionCode = 11 - OptionUnicast OptionCode = 12 - OptionStatusCode OptionCode = 13 - OptionRapidCommit OptionCode = 14 - OptionUserClass OptionCode = 15 - OptionVendorClass OptionCode = 16 - OptionVendorOpts OptionCode = 17 - OptionInterfaceID OptionCode = 18 - OptionReconfMessage OptionCode = 19 - OptionReconfAccept OptionCode = 20 - OptionSIPServersDomainNameList OptionCode = 21 - OptionSIPServersIPv6AddressList OptionCode = 22 - OptionDNSRecursiveNameServer OptionCode = 23 - OptionDomainSearchList OptionCode = 24 - OptionIAPD OptionCode = 25 - OptionIAPrefix OptionCode = 26 - OptionNISServers OptionCode = 27 - OptionNISPServers OptionCode = 28 - OptionNISDomainName OptionCode = 29 - OptionNISPDomainName OptionCode = 30 - OptionSNTPServerList OptionCode = 31 - OptionInformationRefreshTime OptionCode = 32 - OptionBCMCSControllerDomainNameList OptionCode = 33 - OptionBCMCSControllerIPv6AddressList OptionCode = 34 - // skip 35 + OptionClientID OptionCode = 1 + OptionServerID OptionCode = 2 + OptionIANA OptionCode = 3 + OptionIATA OptionCode = 4 + OptionIAAddr OptionCode = 5 + OptionORO OptionCode = 6 + OptionPreference OptionCode = 7 + OptionElapsedTime OptionCode = 8 + OptionRelayMsg OptionCode = 9 + _ OptionCode = 10 + OptionAuth OptionCode = 11 + OptionUnicast OptionCode = 12 + OptionStatusCode OptionCode = 13 + OptionRapidCommit OptionCode = 14 + OptionUserClass OptionCode = 15 + OptionVendorClass OptionCode = 16 + OptionVendorOpts OptionCode = 17 + OptionInterfaceID OptionCode = 18 + OptionReconfMessage OptionCode = 19 + OptionReconfAccept OptionCode = 20 + OptionSIPServersDomainNameList OptionCode = 21 + OptionSIPServersIPv6AddressList OptionCode = 22 + OptionDNSRecursiveNameServer OptionCode = 23 + OptionDomainSearchList OptionCode = 24 + OptionIAPD OptionCode = 25 + OptionIAPrefix OptionCode = 26 + OptionNISServers OptionCode = 27 + OptionNISPServers OptionCode = 28 + OptionNISDomainName OptionCode = 29 + OptionNISPDomainName OptionCode = 30 + OptionSNTPServerList OptionCode = 31 + OptionInformationRefreshTime OptionCode = 32 + OptionBCMCSControllerDomainNameList OptionCode = 33 + OptionBCMCSControllerIPv6AddressList OptionCode = 34 + _ OptionCode = 35 OptionGeoConfCivic OptionCode = 36 OptionRemoteID OptionCode = 37 OptionRelayAgentSubscriberID OptionCode = 38 @@ -154,79 +154,214 @@ const ( OptionMIPv6HomeNetworkPrefix OptionCode = 71 OptionMIPv6HomeAgentAddress OptionCode = 72 OptionMIPv6HomeAgentFQDN OptionCode = 73 + OptionRDNSSSelection OptionCode = 74 + OptionKRBPrincipalName OptionCode = 75 + OptionKRBRealmName OptionCode = 76 + OptionKRBDefaultRealmName OptionCode = 77 + OptionKRBKDC OptionCode = 78 + OptionClientLinkLayerAddr OptionCode = 79 + OptionLinkAddress OptionCode = 80 + OptionRadius OptionCode = 81 + OptionSolMaxRT OptionCode = 82 + OptionInfMaxRT OptionCode = 83 + OptionAddrSel OptionCode = 84 + OptionAddrSelTable OptionCode = 85 + OptionV6PCPServer OptionCode = 86 + OptionDHCPv4Msg OptionCode = 87 + OptionDHCP4oDHCP6Server OptionCode = 88 + OptionS46Rule OptionCode = 89 + OptionS46BR OptionCode = 90 + OptionS46DMR OptionCode = 91 + OptionS46V4V6Bind OptionCode = 92 + OptionS46PortParams OptionCode = 93 + OptionS46ContMapE OptionCode = 94 + OptionS46ContMapT OptionCode = 95 + OptionS46ContLW OptionCode = 96 + Option4RD OptionCode = 97 + Option4RDMapRule OptionCode = 98 + Option4RDNonMapRule OptionCode = 99 + OptionLQBaseTime OptionCode = 100 + OptionLQStartTime OptionCode = 101 + OptionLQEndTime OptionCode = 102 + OptionCaptivePortal OptionCode = 103 + OptionMPLParameters OptionCode = 104 + OptionANIAccessTechType OptionCode = 105 + OptionANINetworkName OptionCode = 106 + OptionANIAccessPointName OptionCode = 107 + OptionANIAccessPointBSSID OptionCode = 108 + OptionANIOperatorID OptionCode = 109 + OptionANIOperatorRealm OptionCode = 110 + OptionS46Priority OptionCode = 111 + OptionMUDUrlV6 OptionCode = 112 + OptionV6Prefix64 OptionCode = 113 + OptionFailoverBindingStatus OptionCode = 114 + OptionFailoverConnectFlags OptionCode = 115 + OptionFailoverDNSRemovalInfo OptionCode = 116 + OptionFailoverDNSHostName OptionCode = 117 + OptionFailoverDNSZoneName OptionCode = 118 + OptionFailoverDNSFlags OptionCode = 119 + OptionFailoverExpirationTime OptionCode = 120 + OptionFailoverMaxUnackedBNDUPD OptionCode = 121 + OptionFailoverMCLT OptionCode = 122 + OptionFailoverPartnerLifetime OptionCode = 123 + OptionFailoverPartnerLifetimeSent OptionCode = 124 + OptionFailoverPartnerDownTime OptionCode = 125 + OptionFailoverPartnerRawCLTTime OptionCode = 126 + OptionFailoverProtocolVersion OptionCode = 127 + OptionFailoverKeepaliveTime OptionCode = 128 + OptionFailoverReconfigureData OptionCode = 129 + OptionFailoverRelationshipName OptionCode = 130 + OptionFailoverServerFlags OptionCode = 131 + OptionFailoverServerState OptionCode = 132 + OptionFailoverStartTimeOfState OptionCode = 133 + OptionFailoverStateExpirationTime OptionCode = 134 + OptionRelayPort OptionCode = 135 + OptionV6SZTPRedirect OptionCode = 136 + OptionS46BindIPv6Prefix OptionCode = 137 + _ OptionCode = 138 + _ OptionCode = 139 + _ OptionCode = 140 + _ OptionCode = 141 + _ OptionCode = 142 + OptionIPv6AddressANDSF OptionCode = 143 ) // optionCodeToString maps DHCPv6 OptionCodes to human-readable strings. var optionCodeToString = map[OptionCode]string{ - OptionClientID: "OPTION_CLIENTID", - OptionServerID: "OPTION_SERVERID", - OptionIANA: "OPTION_IA_NA", - OptionIATA: "OPTION_IA_TA", - OptionIAAddr: "OPTION_IAADDR", - OptionORO: "OPTION_ORO", - OptionPreference: "OPTION_PREFERENCE", - OptionElapsedTime: "OPTION_ELAPSED_TIME", - OptionRelayMsg: "OPTION_RELAY_MSG", - OptionAuth: "OPTION_AUTH", - OptionUnicast: "OPTION_UNICAST", - OptionStatusCode: "OPTION_STATUS_CODE", - OptionRapidCommit: "OPTION_RAPID_COMMIT", - OptionUserClass: "OPTION_USER_CLASS", - OptionVendorClass: "OPTION_VENDOR_CLASS", - OptionVendorOpts: "OPTION_VENDOR_OPTS", - OptionInterfaceID: "OPTION_INTERFACE_ID", - OptionReconfMessage: "OPTION_RECONF_MSG", - OptionReconfAccept: "OPTION_RECONF_ACCEPT", - OptionSIPServersDomainNameList: "SIP Servers Domain Name List", - OptionSIPServersIPv6AddressList: "SIP Servers IPv6 Address List", - OptionDNSRecursiveNameServer: "DNS Recursive Name Server", - OptionDomainSearchList: "Domain Search List", - OptionIAPD: "OPTION_IA_PD", - OptionIAPrefix: "OPTION_IAPREFIX", - OptionNISServers: "OPTION_NIS_SERVERS", - OptionNISPServers: "OPTION_NISP_SERVERS", - OptionNISDomainName: "OPTION_NIS_DOMAIN_NAME", - OptionNISPDomainName: "OPTION_NISP_DOMAIN_NAME", - OptionSNTPServerList: "SNTP Server List", - OptionInformationRefreshTime: "Information Refresh Time", - OptionBCMCSControllerDomainNameList: "BCMCS Controller Domain Name List", - OptionBCMCSControllerIPv6AddressList: "BCMCS Controller IPv6 Address List", - OptionGeoConfCivic: "OPTION_GEOCONF", - OptionRemoteID: "OPTION_REMOTE_ID", - OptionRelayAgentSubscriberID: "Relay-Agent Subscriber ID", - OptionFQDN: "FQDN", - OptionPANAAuthenticationAgent: "PANA Authentication Agent", - OptionNewPOSIXTimezone: "OPTION_NEW_POSIX_TIME_ZONE", - OptionNewTZDBTimezone: "OPTION_NEW_TZDB_TIMEZONE", - OptionEchoRequest: "Echo Request", - OptionLQQuery: "OPTION_LQ_QUERY", - OptionClientData: "OPTION_CLIENT_DATA", - OptionCLTTime: "OPTION_CLT_TIME", - OptionLQRelayData: "OPTION_LQ_RELAY_DATA", - OptionLQClientLink: "OPTION_LQ_CLIENT_LINK", - OptionMIPv6HomeNetworkIDFQDN: "MIPv6 Home Network ID FQDN", - OptionMIPv6VisitedHomeNetworkInformation: "MIPv6 Visited Home Network Information", - OptionLoSTServer: "LoST Server", - OptionCAPWAPAccessControllerAddresses: "CAPWAP Access Controller Addresses", - OptionRelayID: "RELAY_ID", - OptionIPv6AddressMOS: "OPTION-IPv6_Address-MoS", - OptionIPv6FQDNMOS: "OPTION-IPv6-FQDN-MoS", - OptionNTPServer: "OPTION_NTP_SERVER", - OptionV6AccessDomain: "OPTION_V6_ACCESS_DOMAIN", - OptionSIPUACSList: "OPTION_SIP_UA_CS_LIST", - OptionBootfileURL: "OPT_BOOTFILE_URL", - OptionBootfileParam: "OPT_BOOTFILE_PARAM", - OptionClientArchType: "OPTION_CLIENT_ARCH_TYPE", - OptionNII: "OPTION_NII", - OptionGeolocation: "OPTION_GEOLOCATION", - OptionAFTRName: "OPTION_AFTR_NAME", - OptionERPLocalDomainName: "OPTION_ERP_LOCAL_DOMAIN_NAME", - OptionRSOO: "OPTION_RSOO", - OptionPDExclude: "OPTION_PD_EXCLUDE", - OptionVirtualSubnetSelection: "Virtual Subnet Selection", - OptionMIPv6IdentifiedHomeNetworkInformation: "MIPv6 Identified Home Network Information", + OptionClientID: "OPTION_CLIENTID", + OptionServerID: "OPTION_SERVERID", + OptionIANA: "OPTION_IA_NA", + OptionIATA: "OPTION_IA_TA", + OptionIAAddr: "OPTION_IAADDR", + OptionORO: "OPTION_ORO", + OptionPreference: "OPTION_PREFERENCE", + OptionElapsedTime: "OPTION_ELAPSED_TIME", + OptionRelayMsg: "OPTION_RELAY_MSG", + OptionAuth: "OPTION_AUTH", + OptionUnicast: "OPTION_UNICAST", + OptionStatusCode: "OPTION_STATUS_CODE", + OptionRapidCommit: "OPTION_RAPID_COMMIT", + OptionUserClass: "OPTION_USER_CLASS", + OptionVendorClass: "OPTION_VENDOR_CLASS", + OptionVendorOpts: "OPTION_VENDOR_OPTS", + OptionInterfaceID: "OPTION_INTERFACE_ID", + OptionReconfMessage: "OPTION_RECONF_MSG", + OptionReconfAccept: "OPTION_RECONF_ACCEPT", + OptionSIPServersDomainNameList: "SIP Servers Domain Name List", + OptionSIPServersIPv6AddressList: "SIP Servers IPv6 Address List", + OptionDNSRecursiveNameServer: "DNS Recursive Name Server", + OptionDomainSearchList: "Domain Search List", + OptionIAPD: "OPTION_IA_PD", + OptionIAPrefix: "OPTION_IAPREFIX", + OptionNISServers: "OPTION_NIS_SERVERS", + OptionNISPServers: "OPTION_NISP_SERVERS", + OptionNISDomainName: "OPTION_NIS_DOMAIN_NAME", + OptionNISPDomainName: "OPTION_NISP_DOMAIN_NAME", + OptionSNTPServerList: "SNTP Server List", + OptionInformationRefreshTime: "Information Refresh Time", + OptionBCMCSControllerDomainNameList: "BCMCS Controller Domain Name List", + OptionBCMCSControllerIPv6AddressList: "BCMCS Controller IPv6 Address List", + OptionGeoConfCivic: "OPTION_GEOCONF", + OptionRemoteID: "OPTION_REMOTE_ID", + OptionRelayAgentSubscriberID: "Relay-Agent Subscriber ID", + OptionFQDN: "FQDN", + OptionPANAAuthenticationAgent: "PANA Authentication Agent", + OptionNewPOSIXTimezone: "OPTION_NEW_POSIX_TIME_ZONE", + OptionNewTZDBTimezone: "OPTION_NEW_TZDB_TIMEZONE", + OptionEchoRequest: "Echo Request", + OptionLQQuery: "OPTION_LQ_QUERY", + OptionClientData: "OPTION_CLIENT_DATA", + OptionCLTTime: "OPTION_CLT_TIME", + OptionLQRelayData: "OPTION_LQ_RELAY_DATA", + OptionLQClientLink: "OPTION_LQ_CLIENT_LINK", + OptionMIPv6HomeNetworkIDFQDN: "MIPv6 Home Network ID FQDN", + OptionMIPv6VisitedHomeNetworkInformation: "MIPv6 Visited Home Network Information", + OptionLoSTServer: "LoST Server", + OptionCAPWAPAccessControllerAddresses: "CAPWAP Access Controller Addresses", + OptionRelayID: "RELAY_ID", + OptionIPv6AddressMOS: "OPTION-IPv6_Address-MoS", + OptionIPv6FQDNMOS: "OPTION-IPv6-FQDN-MoS", + OptionNTPServer: "OPTION_NTP_SERVER", + OptionV6AccessDomain: "OPTION_V6_ACCESS_DOMAIN", + OptionSIPUACSList: "OPTION_SIP_UA_CS_LIST", + OptionBootfileURL: "OPT_BOOTFILE_URL", + OptionBootfileParam: "OPT_BOOTFILE_PARAM", + OptionClientArchType: "OPTION_CLIENT_ARCH_TYPE", + OptionNII: "OPTION_NII", + OptionGeolocation: "OPTION_GEOLOCATION", + OptionAFTRName: "OPTION_AFTR_NAME", + OptionERPLocalDomainName: "OPTION_ERP_LOCAL_DOMAIN_NAME", + OptionRSOO: "OPTION_RSOO", + OptionPDExclude: "OPTION_PD_EXCLUDE", + OptionVirtualSubnetSelection: "Virtual Subnet Selection", + OptionMIPv6IdentifiedHomeNetworkInformation: "MIPv6 Identified Home Network Information", OptionMIPv6UnrestrictedHomeNetworkInformation: "MIPv6 Unrestricted Home Network Information", OptionMIPv6HomeNetworkPrefix: "MIPv6 Home Network Prefix", OptionMIPv6HomeAgentAddress: "MIPv6 Home Agent Address", OptionMIPv6HomeAgentFQDN: "MIPv6 Home Agent FQDN", + OptionRDNSSSelection: "RDNSS Selection", + OptionKRBPrincipalName: "Kerberos Principal Name", + OptionKRBRealmName: "Kerberos Realm Name", + OptionKRBDefaultRealmName: "Kerberos Default Realm Name", + OptionKRBKDC: "Kerberos KDC", + OptionClientLinkLayerAddr: "Client Link-Layer Address", + OptionLinkAddress: "Link Address", + OptionRadius: "OPTION_RADIUS", + OptionSolMaxRT: "Max Solicit Timeout Value", + OptionInfMaxRT: "Max Information-Request Timeout Value", + OptionAddrSel: "Address Selection", + OptionAddrSelTable: "Address Selection Policy Table", + OptionV6PCPServer: "Port Control Protocol Server", + OptionDHCPv4Msg: "Encapsulated DHCPv4 Message", + OptionDHCP4oDHCP6Server: "DHCPv4-over-DHCPv6 Server", + OptionS46Rule: "Softwire46 Rule", + OptionS46BR: "Softwire46 Border Relay", + OptionS46DMR: "Softwire46 Default Mapping Rule", + OptionS46V4V6Bind: "Softwire46 IPv4/IPv6 Address Binding", + OptionS46PortParams: "Softwire46 Port Parameters", + OptionS46ContMapE: "Softwire46 MAP-E Container", + OptionS46ContMapT: "Softwire46 MAP-T Container", + OptionS46ContLW: "Softwire46 Lightweight 4over6 Container", + Option4RD: "IPv4 Residual Deployment", + Option4RDMapRule: "IPv4 Residual Deployment Mapping Rule", + Option4RDNonMapRule: "IPv4 Residual Deployment Non-Mapping Rule", + OptionLQBaseTime: "Leasequery Server Base time", + OptionLQStartTime: "Leasequery Server Query Start Time", + OptionLQEndTime: "Leasequery Server Query End Time", + OptionCaptivePortal: "Captive Portal URI", + OptionMPLParameters: "MPL Parameters", + OptionANIAccessTechType: "Access-Network-Information Access-Technology-Type", + OptionANINetworkName: "Access-Network-Information Network-Name", + OptionANIAccessPointName: "Access-Network-Information Access-Point-Name", + OptionANIAccessPointBSSID: "Access-Network-Information Access-Point-BSSID", + OptionANIOperatorID: "Access-Network-Information Operator-Identifier", + OptionANIOperatorRealm: "Access-Network-Information Operator-Realm", + OptionS46Priority: "Softwire46 Priority", + OptionMUDUrlV6: "Manufacturer Usage Description URL", + OptionV6Prefix64: "OPTION_V6_PREFIX64", + OptionFailoverBindingStatus: "Failover Binding Status", + OptionFailoverConnectFlags: "Failover Connection Flags", + OptionFailoverDNSRemovalInfo: "Failover DNS Removal Info", + OptionFailoverDNSHostName: "Failover DNS Removal Host Name", + OptionFailoverDNSZoneName: "Failover DNS Removal Zone Name", + OptionFailoverDNSFlags: "Failover DNS Removal Flags", + OptionFailoverExpirationTime: "Failover Maximum Expiration Time", + OptionFailoverMaxUnackedBNDUPD: "Failover Maximum Unacked BNDUPD Messages", + OptionFailoverMCLT: "Failover Maximum Client Lead Time", + OptionFailoverPartnerLifetime: "Failover Partner Lifetime", + OptionFailoverPartnerLifetimeSent: "Failover Received Partner Lifetime", + OptionFailoverPartnerDownTime: "Failover Last Partner Down Time", + OptionFailoverPartnerRawCLTTime: "Failover Last Client Time", + OptionFailoverProtocolVersion: "Failover Protocol Version", + OptionFailoverKeepaliveTime: "Failover Keepalive Time", + OptionFailoverReconfigureData: "Failover Reconfigure Data", + OptionFailoverRelationshipName: "Failover Relationship Name", + OptionFailoverServerFlags: "Failover Server Flags", + OptionFailoverServerState: "Failover Server State", + OptionFailoverStartTimeOfState: "Failover State Start Time", + OptionFailoverStateExpirationTime: "Failover State Expiration Time", + OptionRelayPort: "Relay Source Port", + OptionV6SZTPRedirect: "IPv6 Secure Zerotouch Provisioning Redirect", + OptionS46BindIPv6Prefix: "Softwire46 Source Binding Prefix Hint", + OptionIPv6AddressANDSF: "IPv6 Access Network Discovery and Selection Function Address", } -- cgit v1.2.3 From 51aead750bd8a5a9fb757f63d4663ba1b4be9bb6 Mon Sep 17 00:00:00 2001 From: Anatole Denis Date: Sun, 3 Nov 2019 16:46:39 +0100 Subject: dhcpv6: Add support for 4RD options IPv4 Residual Deployment (4RD) is a strategy for providing IPv4 connectivity in IPv6-only networks. The standard includes autoconfiguration via DHCPv6, as described in RFC7600. This adds support for the 3 options defined in that RFC Signed-off-by: Anatole Denis --- dhcpv6/option_4rd.go | 178 ++++++++++++++++++++++++++++++++++++++++++++++ dhcpv6/option_4rd_test.go | 168 +++++++++++++++++++++++++++++++++++++++++++ dhcpv6/options.go | 6 ++ 3 files changed, 352 insertions(+) create mode 100644 dhcpv6/option_4rd.go create mode 100644 dhcpv6/option_4rd_test.go diff --git a/dhcpv6/option_4rd.go b/dhcpv6/option_4rd.go new file mode 100644 index 0000000..9bac74a --- /dev/null +++ b/dhcpv6/option_4rd.go @@ -0,0 +1,178 @@ +package dhcpv6 + +import ( + "fmt" + "net" + + "github.com/u-root/u-root/pkg/uio" +) + +// Opt4RD represents a 4RD option. It is only a container for 4RD_*_RULE options +type Opt4RD Options + +// Code returns the Option Code for this option +func (op *Opt4RD) Code() OptionCode { + return Option4RD +} + +// ToBytes serializes this option +func (op *Opt4RD) ToBytes() []byte { + return (*Options)(op).ToBytes() +} + +// String returns a human-readable representation of the option +func (op *Opt4RD) String() string { + return fmt.Sprintf("Opt4RD{%v}", (*Options)(op)) +} + +// ParseOpt4RD builds an Opt4RD structure from a sequence of bytes. +// The input data does not include option code and length bytes +func ParseOpt4RD(data []byte) (*Opt4RD, error) { + var opt Options + err := opt.FromBytes(data) + return (*Opt4RD)(&opt), err +} + +// Opt4RDMapRule represents a 4RD Mapping Rule option +// The option is described in https://tools.ietf.org/html/rfc7600#section-4.9 +// The 4RD mapping rules are described in https://tools.ietf.org/html/rfc7600#section-4.2 +type Opt4RDMapRule struct { + // Prefix4 is the IPv4 prefix mapped by this rule + Prefix4 net.IPNet + // Prefix6 is the IPv6 prefix mapped by this rule + Prefix6 net.IPNet + // EABitsLength is the number of bits of an address used in constructing the mapped address + EABitsLength uint8 + // WKPAuthorized determines if well-known ports are assigned to addresses in an A+P mapping + // It can only be set if the length of Prefix4 + EABits > 32 + WKPAuthorized bool +} + +const ( + // opt4RDWKPAuthorizedMask is the mask for the WKPAuthorized flag in its + // byte in Opt4RDMapRule + opt4RDWKPAuthorizedMask = 1 << 7 + // opt4RDHubAndSpokeMask is the mask for the HubAndSpoke flag in its + // byte in Opt4RDNonMapRule + opt4RDHubAndSpokeMask = 1 << 7 + // opt4RDTrafficClassMask is the mask for the TrafficClass flag in its + // byte in Opt4RDNonMapRule + opt4RDTrafficClassMask = 1 << 0 +) + +// Code returns the option code representing this option +func (op *Opt4RDMapRule) Code() OptionCode { return Option4RDMapRule } + +// ToBytes serializes this option +func (op *Opt4RDMapRule) ToBytes() []byte { + buf := uio.NewBigEndianBuffer(nil) + p4Len, _ := op.Prefix4.Mask.Size() + p6Len, _ := op.Prefix6.Mask.Size() + buf.Write8(uint8(p4Len)) + buf.Write8(uint8(p6Len)) + buf.Write8(op.EABitsLength) + if op.WKPAuthorized { + buf.Write8(opt4RDWKPAuthorizedMask) + } else { + buf.Write8(0) + } + if op.Prefix4.IP.To4() == nil { + // The API prevents us from returning an error here + // We just write zeros instead, which is pretty bad behaviour + buf.Write32(0) + } else { + buf.WriteBytes(op.Prefix4.IP.To4()) + } + if op.Prefix6.IP.To16() == nil { + buf.Write64(0) + buf.Write64(0) + } else { + buf.WriteBytes(op.Prefix6.IP.To16()) + } + return buf.Data() +} + +// String returns a human-readable description of this option +func (op *Opt4RDMapRule) String() string { + return fmt.Sprintf("Opt4RDMapRule{Prefix4=%s, Prefix6=%s, EA-Bits=%d, WKPAuthorized=%t}", + op.Prefix4.String(), op.Prefix6.String(), op.EABitsLength, op.WKPAuthorized) +} + +// ParseOpt4RDMapRule builds an Opt4RDMapRule structure from a sequence of bytes. +// The input data does not include option code and length bytes. +func ParseOpt4RDMapRule(data []byte) (*Opt4RDMapRule, error) { + var opt Opt4RDMapRule + buf := uio.NewBigEndianBuffer(data) + opt.Prefix4.Mask = net.CIDRMask(int(buf.Read8()), 32) + opt.Prefix6.Mask = net.CIDRMask(int(buf.Read8()), 128) + opt.EABitsLength = buf.Read8() + opt.WKPAuthorized = (buf.Read8() & opt4RDWKPAuthorizedMask) != 0 + opt.Prefix4.IP = net.IP(buf.CopyN(net.IPv4len)) + opt.Prefix6.IP = net.IP(buf.CopyN(net.IPv6len)) + return &opt, buf.FinError() +} + +// Opt4RDNonMapRule represents 4RD parameters other than mapping rules +type Opt4RDNonMapRule struct { + // HubAndSpoke is whether the network topology is hub-and-spoke or meshed + HubAndSpoke bool + // TrafficClass is an optional 8-bit tunnel traffic class identifier + TrafficClass *uint8 + // DomainPMTU is the Path MTU for this 4RD domain + DomainPMTU uint16 +} + +// Code returns the option code for this option +func (op *Opt4RDNonMapRule) Code() OptionCode { + return Option4RDNonMapRule +} + +// ToBytes serializes this option +func (op *Opt4RDNonMapRule) ToBytes() []byte { + buf := uio.NewBigEndianBuffer(nil) + var flags uint8 + var trafficClassValue uint8 + if op.HubAndSpoke { + flags |= opt4RDHubAndSpokeMask + } + if op.TrafficClass != nil { + flags |= opt4RDTrafficClassMask + trafficClassValue = *op.TrafficClass + } + + buf.Write8(flags) + buf.Write8(trafficClassValue) + buf.Write16(op.DomainPMTU) + + return buf.Data() +} + +// String returns a human-readable description of this option +func (op *Opt4RDNonMapRule) String() string { + var tClass interface{} = false + if op.TrafficClass != nil { + tClass = *op.TrafficClass + } + + return fmt.Sprintf("Opt4RDNonMapRule{HubAndSpoke=%t, TrafficClass=%v, DomainPMTU=%d}", + op.HubAndSpoke, tClass, op.DomainPMTU) +} + +// ParseOpt4RDNonMapRule builds an Opt4RDNonMapRule structure from a sequence of bytes. +// The input data does not include option code and length bytes +func ParseOpt4RDNonMapRule(data []byte) (*Opt4RDNonMapRule, error) { + var opt Opt4RDNonMapRule + buf := uio.NewBigEndianBuffer(data) + flags := buf.Read8() + + opt.HubAndSpoke = flags&opt4RDHubAndSpokeMask != 0 + + tClass := buf.Read8() + if flags&opt4RDTrafficClassMask != 0 { + opt.TrafficClass = &tClass + } + + opt.DomainPMTU = buf.Read16() + + return &opt, buf.FinError() +} diff --git a/dhcpv6/option_4rd_test.go b/dhcpv6/option_4rd_test.go new file mode 100644 index 0000000..1251b13 --- /dev/null +++ b/dhcpv6/option_4rd_test.go @@ -0,0 +1,168 @@ +package dhcpv6 + +import ( + "net" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOpt4RDNonMapRuleParse(t *testing.T) { + data := []byte{0x81, 0xaa, 0x05, 0xd4} + opt, err := ParseOpt4RDNonMapRule(data) + require.NoError(t, err) + require.True(t, opt.HubAndSpoke) + require.NotNil(t, opt.TrafficClass) + require.EqualValues(t, 0xaa, *opt.TrafficClass) + require.EqualValues(t, 1492, opt.DomainPMTU) + + // Remove the TrafficClass flag and check value is ignored + data[0] = 0x80 + opt, err = ParseOpt4RDNonMapRule(data) + require.NoError(t, err) + require.True(t, opt.HubAndSpoke) + require.Nil(t, opt.TrafficClass) + require.EqualValues(t, 1492, opt.DomainPMTU) +} + +func TestOpt4RDNonMapRuleToBytes(t *testing.T) { + var tClass uint8 = 0xaa + opt := Opt4RDNonMapRule{ + HubAndSpoke: true, + TrafficClass: &tClass, + DomainPMTU: 1492, + } + expected := []byte{0x81, 0xaa, 0x05, 0xd4} + + require.Equal(t, expected, opt.ToBytes()) + + // Unsetting TrafficClass should zero the corresponding bytes in the output + opt.TrafficClass = nil + expected[0], expected[1] = 0x80, 0x00 + + require.Equal(t, expected, opt.ToBytes()) +} + +func TestOpt4RDNonMapRuleString(t *testing.T) { + var tClass uint8 = 120 + opt := Opt4RDNonMapRule{ + HubAndSpoke: true, + TrafficClass: &tClass, + DomainPMTU: 9000, + } + + str := opt.String() + + require.Contains(t, str, "HubAndSpoke=true", + "String() should contain the HubAndSpoke flag value") + require.Contains(t, str, "TrafficClass=120", + "String() should contain the TrafficClass flag value") + require.Contains(t, str, "DomainPMTU=9000", + "String() should contain the domain PMTU") +} + +func TestOpt4RDMapRuleParse(t *testing.T) { + ip6addr, ip6net, err := net.ParseCIDR("2001:db8::1234:5678:0:aabb/64") + ip6net.IP = ip6addr // We want to keep the entire address however, not apply the mask + require.NoError(t, err) + ip4addr, ip4net, err := net.ParseCIDR("100.64.0.234/10") + ip4net.IP = ip4addr.To4() + require.NoError(t, err) + data := append([]byte{ + 10, // IPv4 prefix length + 64, // IPv6 prefix length + 32, // EA-bits + 0x80, // WKPs authorized + }, + append(ip4addr.To4(), ip6addr...)..., + ) + + opt, err := ParseOpt4RDMapRule(data) + require.NoError(t, err) + require.EqualValues(t, *ip6net, opt.Prefix6) + require.EqualValues(t, *ip4net, opt.Prefix4) + require.EqualValues(t, 32, opt.EABitsLength) + require.True(t, opt.WKPAuthorized) +} + +func TestOpt4RDMapRuleToBytes(t *testing.T) { + opt := Opt4RDMapRule{ + Prefix4: net.IPNet{ + IP: net.IPv4(100, 64, 0, 238), + Mask: net.CIDRMask(24, 32), + }, + Prefix6: net.IPNet{ + IP: net.ParseIP("2001:db8::1234:5678:0:aabb"), + Mask: net.CIDRMask(80, 128), + }, + EABitsLength: 32, + WKPAuthorized: true, + } + + expected := append([]byte{ + 24, // v4 prefix length + 80, // v6 prefix length + 32, // EA-bits + 0x80, // WKPs authorized + }, + append(opt.Prefix4.IP.To4(), opt.Prefix6.IP.To16()...)..., + ) + + require.Equal(t, expected, opt.ToBytes()) +} + +// FIXME: Invalid packets are serialized without error + +func TestOpt4RDMapRuleString(t *testing.T) { + opt := Opt4RDMapRule{ + Prefix4: net.IPNet{ + IP: net.IPv4(100, 64, 0, 238), + Mask: net.CIDRMask(24, 32), + }, + Prefix6: net.IPNet{ + IP: net.ParseIP("2001:db8::1234:5678:0:aabb"), + Mask: net.CIDRMask(80, 128), + }, + EABitsLength: 32, + WKPAuthorized: true, + } + + str := opt.String() + require.Contains(t, str, "WKPAuthorized=true", "String() should write the flag values") + require.Contains(t, str, "Prefix6=2001:db8::1234:5678:0:aabb/80", + "String() should include the IPv6 prefix") + require.Contains(t, str, "Prefix4=100.64.0.238/24", + "String() should include the IPv4 prefix") + require.Contains(t, str, "EA-Bits=32", "String() should include the value for EA-Bits") +} + +// This test round-trip serialization/deserialization of both kinds of 4RD +// options, and the container option +func TestOpt4RDRoundTrip(t *testing.T) { + var tClass uint8 = 0xaa + opt := Opt4RD{ + &Opt4RDMapRule{ + Prefix4: net.IPNet{ + IP: net.IPv4(100, 64, 0, 238).To4(), + Mask: net.CIDRMask(24, 32), + }, + Prefix6: net.IPNet{ + IP: net.ParseIP("2001:db8::1234:5678:0:aabb"), + Mask: net.CIDRMask(80, 128), + }, + EABitsLength: 32, + WKPAuthorized: true, + }, + &Opt4RDNonMapRule{ + HubAndSpoke: true, + TrafficClass: &tClass, + DomainPMTU: 9000, + }, + } + + rtOpt, err := ParseOpt4RD(opt.ToBytes()) + + require.NoError(t, err) + require.NotNil(t, rtOpt) + require.Equal(t, opt, *rtOpt) +} diff --git a/dhcpv6/options.go b/dhcpv6/options.go index 47ccdca..cf192fc 100644 --- a/dhcpv6/options.go +++ b/dhcpv6/options.go @@ -79,6 +79,12 @@ func ParseOption(code OptionCode, optData []byte) (Option, error) { opt, err = ParseOptClientArchType(optData) case OptionNII: opt, err = ParseOptNetworkInterfaceId(optData) + case Option4RD: + opt, err = ParseOpt4RD(optData) + case Option4RDMapRule: + opt, err = ParseOpt4RDMapRule(optData) + case Option4RDNonMapRule: + opt, err = ParseOpt4RDNonMapRule(optData) default: opt = &OptionGeneric{OptionCode: code, OptionData: optData} } -- cgit v1.2.3