private static bool TryReadEntry(byte[] buffer, int payloadOffset, int payloadLength, GreSourceRouteEntryAddressFamily addressFamily, int offsetInPayload, out GreSourceRouteEntry entry)
        {
            entry = null;
            switch (addressFamily)
            {
            case GreSourceRouteEntryAddressFamily.IpSourceRoute:
                if (offsetInPayload % IpV4Address.SizeOf != 0 || payloadLength % IpV4Address.SizeOf != 0)
                {
                    return(false);
                }

                int           numAddresses = payloadLength / IpV4Address.SizeOf;
                IpV4Address[] addresses    = new IpV4Address[numAddresses];
                for (int i = 0; i != numAddresses; ++i)
                {
                    addresses[i] = buffer.ReadIpV4Address(payloadOffset + i * IpV4Address.SizeOf, Endianity.Big);
                }

                entry = new GreSourceRouteEntryIp(addresses, offsetInPayload / IpV4Address.SizeOf);
                return(true);

            case GreSourceRouteEntryAddressFamily.AsSourceRoute:
                if (offsetInPayload % sizeof(ushort) != 0 || payloadLength % sizeof(ushort) != 0)
                {
                    return(false);
                }

                int      numAsNumbers = payloadLength / sizeof(ushort);
                ushort[] asNumbers    = new ushort[numAsNumbers];
                for (int i = 0; i != numAsNumbers; ++i)
                {
                    asNumbers[i] = buffer.ReadUShort(payloadOffset + i * sizeof(ushort), Endianity.Big);
                }

                entry = new GreSourceRouteEntryAs(asNumbers, offsetInPayload / sizeof(ushort));
                return(true);

            default:
                Datagram data = new Datagram(buffer, payloadOffset, payloadLength);
                entry = new GreSourceRouteEntryUnknown(addressFamily, data, offsetInPayload);
                return(true);
            }
        }
        public static GreLayer NextGreLayer(this Random random)
        {
            GreVersion version = random.NextEnum<GreVersion>();

            bool isChecksum = random.NextBool();
            GreSourceRouteEntry[] routing = null;
            ushort? routingOffset = null;
            bool strictSourceRoute = false;
            EthernetType protocolType = random.NextEnum(EthernetType.None);
            uint? key = random.NextBool() ? (uint?)random.NextUInt() : null;
            if (version == GreVersion.Gre)
            {
                if (random.NextBool())
                {
                    strictSourceRoute = random.NextBool();
                    routing = new GreSourceRouteEntry[random.Next(5)];

                    GreSourceRouteEntryAddressFamily family;
                    if (random.NextBool())
                        family = random.NextEnum(GreSourceRouteEntryAddressFamily.None);
                    else
                        family = (GreSourceRouteEntryAddressFamily)random.NextUShort();

                    for (int i = 0; i != routing.Length; ++i)
                    {
                        switch (family)
                        {
                            case GreSourceRouteEntryAddressFamily.AsSourceRoute:
                            {
                                ushort[] asNumbers = ((Func<ushort>)(() => random.NextUShort())).GenerateArray(random.NextInt(1, 5));
                                routing[i] = new GreSourceRouteEntryAs(asNumbers.AsReadOnly(), random.Next(asNumbers.Length + 1));
                                break;
                            }

                            case GreSourceRouteEntryAddressFamily.IpSourceRoute:
                            {
                                IpV4Address[] ips = ((Func<IpV4Address>)(() => random.NextIpV4Address())).GenerateArray(random.NextInt(1, 5));
                                routing[i] = new GreSourceRouteEntryIp(ips.AsReadOnly(), random.Next(ips.Length + 1));
                                break;
                            }

                            default:
                            {
                                int dataLength = random.NextInt(1, 100);
                                routing[i] = new GreSourceRouteEntryUnknown(family, random.NextDatagram(dataLength), random.Next(dataLength + 1));
                                break;
                            }
                        }

                    }
                    routingOffset = 0;
                    if (routing.Any())
                    {
                        int routingIndex = random.Next(routing.Length);
                        for (int i = 0; i != routingIndex; ++i)
                            routingOffset += (ushort)routing[i].Length;
                    }
                }
            }
            else
            {
                protocolType = EthernetType.PointToPointProtocol;
                isChecksum = false;
                key = random.NextUInt();
            }

            return new GreLayer
                   {
                       Version = version,
                       ProtocolType = protocolType,
                       ChecksumPresent = isChecksum,
                       Checksum = isChecksum && random.NextBool() ? (ushort?)random.NextUShort() : null,
                       Key = key,
                       SequenceNumber = random.NextBool() ? (uint?)random.NextUInt() : null,
                       AcknowledgmentSequenceNumber = version == GreVersion.EnhancedGre && random.NextBool() ? (uint?)random.NextUInt() : null,
                       RecursionControl = random.NextByte(8),
            //                       Flags = random.NextByte(32),
                       Routing = routing == null ? null : routing.AsReadOnly(),
                       RoutingOffset = routingOffset,
                       StrictSourceRoute = strictSourceRoute,
                   };
        }