C# 对于 IPV4/TCP/UDP/ICMP 网络协议的处理

54 篇文章 2 订阅
17 篇文章 2 订阅

 UDP

namespace Ppp.Net.Udp
{
    using System;
    using System.Net;
    using System.Net.Sockets;
    using Ppp.Core;
    using Ppp.Net.IP;

    public class UdpFrame : EventArgs
    {
        public virtual IPEndPoint Source { get; set; }

        public virtual IPEndPoint Destination { get; set; }

        public virtual AddressFamily AddressFamily { get; }

        public virtual BufferSegment Payload { get; }

        public virtual int Ttl { get; set; }
 
        public UdpFrame(IPEndPoint source, IPEndPoint destination, BufferSegment payload)
        {
            this.Ttl = IPFrame.DefaultTtl;
            this.Source = source ?? throw new ArgumentNullException(nameof(source));
            this.Destination = destination ?? throw new ArgumentNullException(nameof(Destination));
            this.AddressFamily = destination.AddressFamily;
            if (source.AddressFamily != destination.AddressFamily)
            {
                throw new ArgumentOutOfRangeException("The original address is inconsistent with the target address protocol.");
            }
            this.Payload = payload ?? throw new ArgumentNullException(nameof(payload));
        }

        public virtual UdpFrame Depth()
        {
            return new UdpFrame(this.Source, this.Destination, this.Payload.Depth())
            {
                Ttl = this.Ttl
            }; 
        }

        public override string ToString()
        {
            return string.Format($"{Source} -> {Destination}");
        }
    }
}
namespace Ppp.Net.Udp
{
    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Runtime.InteropServices;
    using Ppp.Core;
    using Ppp.Net.Entry;
    using Ppp.Net.IP;

    public unsafe class UdpLayer
    {
        public virtual ILayerLocator Locator { get; }

        public UdpLayer(ILayerLocator locator)
        {
            this.Locator = locator ?? throw new ArgumentNullException(nameof(locator));
        }

        public virtual void Input(UdpFrame frame)
        {
            Output(new UdpFrame(frame.Destination, frame.Source, frame.Payload) { Ttl = frame.Ttl });
        }

        public static IPFrame ToIPFrame(UdpFrame frame)
        {
            if (frame == null)
            {
                throw new ArgumentNullException(nameof(frame));
            }
            if (frame.AddressFamily != AddressFamily.InterNetwork)
            {
                throw new ArgumentNullException("UDP frames of this address family type are not supported.");
            }
            int payload_offset = sizeof(udp_hdr);
            int payload_size = frame.Payload.Length;
            byte[] message = new byte[payload_offset + payload_size];
            fixed (byte* pinned = message)
            {
                udp_hdr* udphdr = (udp_hdr*)pinned;
                udphdr->src = CheckSum.htons((ushort)frame.Source.Port);
                udphdr->dest = CheckSum.htons((ushort)frame.Destination.Port);
                udphdr->len = CheckSum.htons((ushort)message.Length);
                udphdr->chksum = 0;

                Buffer.BlockCopy(frame.Payload.Buffer,
                    frame.Payload.Offset,
                    message,
                    payload_offset,
                    payload_size);

                ushort pseudo_checksum = CheckSum.inet_chksum_pseudo(pinned,
                    (uint)ProtocolType.Udp,
                    (uint)message.Length,
                    IPFrame.GetAddressV4(frame.Source.Address),
                    IPFrame.GetAddressV4(frame.Destination.Address));
                if (pseudo_checksum == 0)
                {
                    pseudo_checksum = 0xffff;
                }

                udphdr->chksum = pseudo_checksum;
            }
            return new IPFrame(ProtocolType.Udp,
                frame.Source.Address,
                frame.Destination.Address,
                new BufferSegment(message))
            {
                Ttl = frame.Ttl,
                Tos = 0x04,
                Flags = 0x00,
            };
        }

        public virtual void Output(UdpFrame frame)
        {
            IPFrame ip = ToIPFrame(frame);
            if (ip != null)
            {
                Locator.IPv4.Output(ip);
            }
        }

        [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 8)]
        private struct udp_hdr
        {
            [FieldOffset(0)]
            public ushort src;
            [FieldOffset(2)]
            public ushort dest;  /* src/dest UDP ports */
            [FieldOffset(4)]
            public ushort len;
            [FieldOffset(6)]
            public ushort chksum;
        }

        public virtual UdpFrame Parse(IPFrame ip) => ParseFrame(ip);

        public static UdpFrame ParseFrame(IPFrame ip, bool checksum = true)
        {
            if (ip == null)
            {
                return null;
            }

            UdpFrame frame = null;
            BufferSegment messages = ip.Payload;
            messages.UnsafeAddrOfPinnedArrayElement((p) =>
            {
                udp_hdr* udphdr = (udp_hdr*)p;
                if (udphdr == null)
                {
                    return;
                }

                if (messages.Length != CheckSum.ntohs(udphdr->len)) // 错误的数据报
                {
                    return;
                }

                int offset = sizeof(udp_hdr);
                int len = messages.Length - offset;
                if (len <= 0)
                {
                    return;
                }

                if (checksum && udphdr->chksum != 0)
                {
                    uint pseudo_checksum = CheckSum.inet_chksum_pseudo((byte*)p.ToPointer(),
                        (uint)ProtocolType.Udp,
                        (uint)messages.Length,
                        ip.SourceAddressV4,
                        ip.DestinationAddressV4);
                    if (pseudo_checksum != 0)
                    {
                        return;
                    }
                }

                BufferSegment message = new BufferSegment(messages.Buffer, messages.Offset + offset, len);
                frame = new UdpFrame(
                    new IPEndPoint(ip.Source, CheckSum.ntohs(udphdr->src)),
                    new IPEndPoint(ip.Destination, CheckSum.ntohs(udphdr->dest)), message)
                    {
                        Ttl = ip.Ttl,
                    };
            });
            return frame;
        }
    }
}

TCP

namespace Ppp.Net.Tcp
{
    using System.Net;
    using Ppp.Core;
    using Ppp.Net.Udp;

    public enum TcpFlags
    {
        TCP_FIN = 0x01,
        TCP_SYN = 0x02,
        TCP_RST = 0x04,
        TCP_PSH = 0x08,
        TCP_ACK = 0x10,
        TCP_UGR = 0x20,
        TCP_ECE = 0x40,
        TCP_CWR = 0x80,
        TCP_FLAGS = 0x3f
    }

    public class TcpFrame : UdpFrame
    {
        public new static readonly BufferSegment Empty = new BufferSegment(BufferSegment.Empty);

        public virtual TcpFlags Flags { get; set; }

        public virtual uint SequenceNo { get; set; }

        public virtual uint AcknowledgeNo { get; set; }

        public virtual ushort WindowSize { get; set; }

        public virtual BufferSegment Options { get; set; }

        public virtual ushort UrgentPointer { get; set; }

        public TcpFrame(IPEndPoint source, IPEndPoint destination, BufferSegment payload) : base(source, destination, payload)
        {

        }
    }
}
namespace Ppp.Net.Tcp
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Runtime.InteropServices;
    using System.Threading;
    using Ppp.Core;
    using Ppp.Net.Entry;
    using Ppp.Net.IP;
#if NET40
    using System.Collections.Concurrent;
#endif

    class SegmentsContext
    {
        public uint AcknowledgeNo;
        public uint SequenceNo;
        public TcpFlags Flags;
        public uint Length;
        public int Retransmission;
        public int Counter;
        public Stopwatch Stopwatch;
        public TcpPcb Pcb;
        public bool Timeout;
        public BufferSegment Payload = TcpFrame.Empty;

        public TcpFrame CreateFrame(TcpPcb pcb)
        {
            return new TcpFrame(pcb.Destination, pcb.Source, this.Payload)
            {
                AcknowledgeNo = this.AcknowledgeNo,
                SequenceNo = this.SequenceNo,
                Flags = this.Flags,
                Ttl = pcb.Ttl,
                WindowSize = (ushort)pcb.ReceiveBufferSize,
            };
        }
    }

    public unsafe class TcpLayer
    {
        private IDictionary<string, TcpPcb> pcbTable =
#if NET40
            new ConcurrentDictionary<string, TcpPcb>();
#else
            new Dictionary<string, TcpPcb>();
#endif
        private bool disposed = false;

        /*
         * typedef struct _tcp_hdr  
         * {  
         *     unsigned short src_port;    //源端口号   
         *     unsigned short dst_port;    //目的端口号   
         *     unsigned int seq_no;        //序列号   
         *     unsigned int ack_no;        //确认号   
         *     #if LITTLE_ENDIAN   
         *     unsigned char reserved_1:4; //保留6位中的4位首部长度   
         *     unsigned char thl:4;        //tcp头部长度   
         *     unsigned char flag:6;       //6位标志   
         *     unsigned char reseverd_2:2; //保留6位中的2位   
         *     #else   
         *     unsigned char thl:4;        //tcp头部长度   
         *     unsigned char reserved_1:4; //保留6位中的4位首部长度   
         *     unsigned char reseverd_2:2; //保留6位中的2位   
         *     unsigned char flag:6;       //6位标志    
         *     #endif   
         *     unsigned short wnd_size;    //16位窗口大小   
         *     unsigned short chk_sum;     //16位TCP检验和   
         *     unsigned short urgt_p;      //16为紧急指针   
         * }tcp_hdr;  
         */

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct tcp_hdr
        {
            public ushort src;
            public ushort dest;
            public uint seqno;
            public uint ackno;
            public ushort _hdrlen_rsvd_flags;
            public ushort wnd;
            public ushort chksum;
            public ushort urgp; // 应用层不可能出现“URGP/UGR or OPT”的协议;这类紧急协议数据报文直接RST链接即可。
        }

        private const int TCP_HLEN = 20;

        private static ushort TCPH_HDRLEN(tcp_hdr* phdr)
        {
            return ((ushort)(CheckSum.ntohs((phdr)->_hdrlen_rsvd_flags) >> 12));
        }

        private static byte TCPH_HDRLEN_BYTES(tcp_hdr* phdr)
        {
            return ((byte)(TCPH_HDRLEN(phdr) << 2));
        }

        private static byte TCPH_FLAGS(tcp_hdr* phdr)
        {
            return ((byte)((CheckSum.ntohs((phdr)->_hdrlen_rsvd_flags) & (byte)TcpFlags.TCP_FLAGS)));
        }

        private static ushort TCPH_HDRLEN_SET(tcp_hdr* phdr, int len)
        {
            var u = ((len) << 12) | TCPH_FLAGS(phdr);
            return (phdr)->_hdrlen_rsvd_flags = CheckSum.htons((ushort)u);
        }

        private static ushort PP_HTONS(int x)
        {
            return ((ushort)((((x) & (ushort)0x00ffU) << 8) | (((x) & (ushort)0xff00U) >> 8)));
        }

        private static ushort TCPH_FLAGS_SET(tcp_hdr* phdr, int flags)
        {
            return (phdr)->_hdrlen_rsvd_flags = (ushort)(((phdr)->_hdrlen_rsvd_flags &
                PP_HTONS(~(ushort)TcpFlags.TCP_FLAGS)) | CheckSum.htons((ushort)flags));
        }

        public virtual ILayerLocator Locator { get; }

        public TcpLayer(ILayerLocator locator)
        {
            this.Locator = locator ?? throw new ArgumentNullException(nameof(locator));
            new Thread(WorkThread) { IsBackground = true, Priority = ThreadPriority.Lowest }.Start();
        }

        private void WorkThread(object state)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            while (!this.disposed)
            {
                if (stopwatch.ElapsedTicks < TcpPcb.MIN_RTO)
                {
                    Thread.Sleep(1);
                    continue;
                }
                else
                {
                    stopwatch.Reset();
                    stopwatch.Start();
                }
                lock (this.pcbTable)
                {
                    foreach (var pair in pcbTable.ToList())
                    {
                        TcpPcb pcb = pair.Value;
                        if (pcb == null)
                        {
                            continue;
                        }

                        SortedDictionary<long, SegmentsContext> segments = pcb.SegmentsContexts;
                        if (segments == null)
                        {
                            continue;
                        }

                        lock (segments)
                        {
                            var remove_segments_keys = new List<long>();
                            foreach (var segments_pair in segments)
                            {
                                SegmentsContext segments_context = segments_pair.Value;
                                if (segments_context == null)
                                {
                                    continue;
                                }

                                double rto_radix = Math.Pow(1.5, Math.Min(segments_context.Retransmission, (1 + segments_context.Counter)));
                                if (segments_context.Stopwatch.ElapsedMilliseconds >= (Math.Max(pcb.RTO, TcpPcb.MIN_RTO)
                                    * rto_radix))
                                {
                                    if (segments_context.Counter++ < segments_context.Retransmission)
                                    {
                                        segments_context.Stopwatch.Reset();
                                        segments_context.Stopwatch.Start();
                                        this.Output(segments_context.CreateFrame(pcb));
                                    }
                                    else
                                    {
                                        remove_segments_keys.Add(segments_pair.Key);
                                    }
                                }
                            }

                            foreach (var segments_key in remove_segments_keys)
                            {
                                pcb.SegmentsContexts.Remove(segments_key, out SegmentsContext segments_x);
                            }
                        }
                    }
                }
            }
        }

        private void ClosePCB(TcpPcb pcb, TcpState state)
        {
            if (pcb == null)
            {
                return;
            }
            string pcbKey = GetPcbKey(pcb.Source, pcb.Destination);
            lock (pcbTable)
            {
                pcbTable.Remove(pcbKey, out TcpPcb pcbx);
            }
            lock (pcb)
            {
                pcb.State = state;
                if (!pcb.Aborted)
                {
                    pcb.Aborted = true;
                    pcb.OnAbort(EventArgs.Empty);
                }
            }
        }

        private static string GetPcbKey(IPEndPoint source, IPEndPoint destination)
        {
            string key = $"{source} <-> {destination}";
            return key;
        }

        private void RST(TcpPcb pcb, TcpFrame frame)
        {
            uint seqno = frame.AcknowledgeNo;
            uint ackno = frame.SequenceNo + 1;
            pcb.State = TcpState.LAST_ACK;
            pcb.Post(TcpFlags.TCP_RST, ackno, seqno);
            ClosePCB(pcb, TcpState.CLOSED);
        }

        public virtual void Input(TcpFrame frame)
        {
            string pcbKey = GetPcbKey(frame.Source, frame.Destination);
            TcpPcb pcb = null;
            lock (pcbTable)
            {
                pcbTable.TryGetValue(pcbKey, out pcb);
                if (pcb == null)
                {
                    pcb = new TcpPcb(frame, this.Locator)
                    {
                        State = TcpState.SYN_RCVD
                    };
                    if (0 == (frame.Flags & TcpFlags.TCP_SYN) || // 不接受此套接字则积极拒绝
                        !this.Locator.Sockets.BeginAccept(pcb))
                    {
                        RST(pcb, frame);
                        return;
                    }

                    pcbTable.TryAdd(pcbKey, pcb);
                    pcb.Open += (sender, e) =>
                    {
                        if (!pcb.Estableshed)
                        {
                            pcb.Estableshed = true;
                            var socket = this.Locator.Sockets.EndAccept(pcb);
                            if (socket == null)
                            {
                                pcb.Close();
                            }
                            else
                            {
                                pcb.OnOpen(e);
                            }
                        }
                    };
                    pcb.Abort += (sender, e) => ClosePCB(sender as TcpPcb, TcpState.CLOSED);
                }
            }
            lock (pcb)
            {
                if (0 != (frame.Flags & TcpFlags.TCP_SYN))
                {
                    uint seqno = pcb.AcknowledgeNo++;
                    uint ackno = ++pcb.SequenceNo;
                    pcb.Post(TcpFlags.TCP_SYN | TcpFlags.TCP_ACK, ackno, seqno, 1, 3);
                }
                else if (0 != (frame.Flags & TcpFlags.TCP_RST) ||
                    0 != (frame.Flags & (TcpFlags.TCP_CWR | TcpFlags.TCP_ECE | TcpFlags.TCP_UGR)))
                {
                    RST(pcb, frame);
                }
                else
                {
                    pcb.SendBufferSize = frame.WindowSize;
                    if (0 != (frame.Flags & TcpFlags.TCP_ACK))
                    {
                        pcb.Ack(frame.AcknowledgeNo);
                    }

                    if (0 != (frame.Flags & TcpFlags.TCP_PSH))
                    {
                        uint pylen = (uint)frame.Payload.Length;
                        uint seqno = frame.AcknowledgeNo;
                        uint ackno = frame.SequenceNo + pylen;

                        if (ackno >= pcb.SequenceNo)
                        {
                            pcb.InputStream.Input(frame);
                        }
                        else
                        {
                            pcb.Post(TcpFlags.TCP_ACK, ackno, seqno, 0);
                        }
                    }
                    else if (0 != (frame.Flags & TcpFlags.TCP_FIN))
                    {
                        uint seqno = frame.AcknowledgeNo;
                        uint ackno = frame.SequenceNo + 1;

                        pcb.Post(TcpFlags.TCP_ACK, ackno, seqno, 0);
                        ClosePCB(pcb, TcpState.CLOSED);
                    }
                }
            }
        }

        public virtual void Output(TcpFrame frame)
        {
            IPFrame ip = ToIPFrame(frame);
            if (ip != null)
            {
                Locator.IPv4.Output(ip);
            }
        }

        public static IPFrame ToIPFrame(TcpFrame frame)
        {
            if (frame == null)
            {
                throw new ArgumentNullException(nameof(frame));
            }

            if (frame.AddressFamily != AddressFamily.InterNetwork)
            {
                throw new ArgumentNullException("TCP frames of this address family type are not supported.");
            }

            BufferSegment options_data = frame.Options;
            int options_size = options_data?.Length ?? 0;
            int payload_offset = sizeof(tcp_hdr) + options_size;
            int payload_size = frame.Payload?.Length ?? 0;

            byte[] message = new byte[payload_offset + payload_size];
            fixed (byte* pinned = message)
            {
                tcp_hdr* tcphdr = (tcp_hdr*)pinned;
                tcphdr->dest = CheckSum.htons((ushort)frame.Destination.Port);
                tcphdr->src = CheckSum.htons((ushort)frame.Source.Port);
                tcphdr->seqno = CheckSum.htonl(frame.SequenceNo);
                tcphdr->ackno = CheckSum.htonl(frame.AcknowledgeNo);
                tcphdr->urgp = CheckSum.htons(frame.UrgentPointer);
                tcphdr->wnd = CheckSum.htons(frame.WindowSize);

                TCPH_HDRLEN_SET(tcphdr, payload_offset >> 2);
                TCPH_FLAGS_SET(tcphdr, (int)frame.Flags);

                if (options_size > 0)
                {
                    IntPtr destination_options = (IntPtr)(pinned + sizeof(tcp_hdr));
                    Marshal.Copy(options_data.Buffer, options_data.Offset, destination_options, options_size);
                }

                if (payload_size > 0)
                {
                    using (MemoryStream ms = new MemoryStream(message, payload_offset, payload_size))
                    {
                        ms.Write(frame.Payload.Buffer, frame.Payload.Offset, payload_size);
                    }
                }

                ushort pseudo_checksum = CheckSum.inet_chksum_pseudo(pinned, (uint)ProtocolType.Tcp, (uint)message.Length,
                        IPFrame.GetAddressV4(frame.Source.Address),
                        IPFrame.GetAddressV4(frame.Destination.Address));
                if (pseudo_checksum == 0)
                {
                    pseudo_checksum = 0xffff;
                }

                tcphdr->chksum = pseudo_checksum;
            }

            return new IPFrame(ProtocolType.Tcp, frame.Source.Address, frame.Destination.Address, new BufferSegment(message))
            {
                Ttl = frame.Ttl,
            };
        }

        public static TcpFrame ParseFrame(IPFrame ip, bool checksum = true)
        {
            if (ip == null)
            {
                return null;
            }

            TcpFrame frame = null;
            BufferSegment packet = ip.Payload;
            packet.UnsafeAddrOfPinnedArrayElement((p) =>
            {
                tcp_hdr* tcphdr = (tcp_hdr*)p;
                if (tcphdr == null)
                {
                    return;
                }

                int hdrlen_bytes = TCPH_HDRLEN_BYTES(tcphdr);
                if (hdrlen_bytes < TCP_HLEN || hdrlen_bytes > packet.Length) // 错误的数据报
                {
                    return;
                }

                int len = packet.Length - hdrlen_bytes;
                if (len < 0)
                {
                    return;
                }

                TcpFlags flags = (TcpFlags)TCPH_FLAGS(tcphdr);
                if (checksum && tcphdr->chksum != 0)
                {
                    uint pseudo_checksum = CheckSum.inet_chksum_pseudo((byte*)p.ToPointer(),
                        (uint)ProtocolType.Tcp,
                        (uint)packet.Length,
                        ip.SourceAddressV4,
                        ip.DestinationAddressV4);
                    if (pseudo_checksum != 0)
                    {
                        return;
                    }
                }

                long payload_offset = 0;
                fixed (byte* stream = packet.Buffer)
                {
                    payload_offset = ((byte*)p + hdrlen_bytes) - stream;
                }

                BufferSegment message_data = new BufferSegment(packet.Buffer, unchecked((int)payload_offset), len);
                BufferSegment options_data = null;
                int options_size = hdrlen_bytes - sizeof(tcp_hdr);
                if (options_size <= 0)
                {
                    options_data = new BufferSegment(BufferSegment.Empty);
                }
                else
                {
                    options_data = new BufferSegment(packet.Buffer,
                            packet.Offset + sizeof(tcp_hdr), options_size);
                }
                frame = new TcpFrame(new IPEndPoint(ip.Source, CheckSum.ntohs(tcphdr->src)), new IPEndPoint(ip.Destination, CheckSum.ntohs(tcphdr->dest)), message_data)
                {
                    Ttl = ip.Ttl,
                    AcknowledgeNo = CheckSum.ntohl(tcphdr->ackno),
                    SequenceNo = CheckSum.ntohl(tcphdr->seqno),
                    WindowSize = CheckSum.ntohs(tcphdr->wnd),
                    Flags = flags,
                    Options = options_data,
                    UrgentPointer = CheckSum.ntohs(tcphdr->urgp)
                };
            });
            return frame;
        }

        public virtual TcpFrame Parse(IPFrame ip) => ParseFrame(ip);
    }
}
namespace Ppp.Net.Tcp
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Net;
    using System.Net.Sockets;
    using Ppp.Core;
    using Ppp.Net;
    using Ppp.Net.Entry;

    public enum TcpState : byte
    {
        CLOSED = 0,
        LISTEN = 1,
        SYN_SENT = 2,
        SYN_RCVD = 3,
        ESTABLISHED = 4,
        FIN_WAIT_1 = 5,
        FIN_WAIT_2 = 6,
        CLOSE_WAIT = 7,
        CLOSING = 8,
        LAST_ACK = 9,
        TIME_WAIT = 10
    }

    class TcpInputStream
    {
        public readonly LinkedList<TcpFrame> Frames = new LinkedList<TcpFrame>();
        public event EventHandler<TcpFrame> Receive;
        public readonly TcpPcb Pcb;

        public TcpInputStream(TcpPcb pcb)
        {
            this.Pcb = pcb ?? throw new ArgumentNullException(nameof(pcb));
        }

        public virtual void Input(TcpFrame frame)
        {
            if (frame == null || frame.Payload == null)
            {
                return;
            }

            if (frame.Payload.Length <= 0)
            {
                return;
            }

            lock (this.Frames)
            {
                if (frame.SequenceNo == this.Pcb.SequenceNo)
                {
                    this.Pcb.SequenceNo = frame.SequenceNo + (uint)frame.Payload.Length;
                    OnReceive(frame);

                    var node = this.Frames.First;
                    while (node != null)
                    {
                        TcpFrame f = node.Value;
                        var current = node;
                        node = current.Next;

                        if (f.SequenceNo == this.Pcb.SequenceNo)
                        {
                            this.Frames.Remove(current);
                            this.Pcb.SequenceNo = f.SequenceNo + (uint)f.Payload.Length;

                            OnReceive(f);
                        }
                    }
                }
                else if (this.Frames.Count <= 0)
                {
                    this.Frames.AddLast(frame);
                }
                else
                {
                    var node = this.Frames.Last;
                    if (frame.SequenceNo == node.Value.SequenceNo)
                    {
                        return;
                    }
                    else if (frame.SequenceNo > node.Value.SequenceNo)
                    {
                        this.Frames.AddLast(frame);
                    }
                    else
                    {
                        node = this.Frames.Last;
                        while (node != null)
                        {
                            TcpFrame f = node.Value;
                            if (f.SequenceNo == frame.SequenceNo)
                            {
                                return;
                            }
                            if (f.SequenceNo < frame.SequenceNo)
                            {
                                this.Frames.AddAfter(node, frame);
                                break;
                            }
                            node = node.Previous;
                        }
                        if (node == null)
                        {
                            this.Frames.AddFirst(frame);
                        }
                    }
                }
            }
        }

        protected virtual void OnReceive(TcpFrame frame)
        {
            this.Receive?.Invoke(this, frame);
        }
    }

    class TcpPcb : IPcb
    {
        public ILayerLocator Locator;
        public TcpState State;
        public uint SequenceNo;
        public uint AcknowledgeNo;
        public uint SendBufferSize;
        public uint ReceiveBufferSize;
        public IPEndPoint Source;
        public IPEndPoint Destination;
        public int Ttl;
        public bool Estableshed;
        public bool Aborted;

        public double RTT;
        public double SRTT;
        public double RTO;
        public double RTTVAL;

        public override EndPoint LocalEndPoint => this.Source;

        public override EndPoint RemoteEndPoint => this.Destination;

        public override AddressFamily AddressFamily { get; }

        public const int MIN_RTO = 200;

        public SortedDictionary<long, SegmentsContext> SegmentsContexts = new SortedDictionary<long, SegmentsContext>();
        public TcpInputStream InputStream;

        public TcpPcb(TcpFrame frame, ILayerLocator locator)
        {
            if (frame == null)
            {
                throw new ArgumentNullException(nameof(frame));
            }

            this.Locator = locator ?? throw new ArgumentNullException(nameof(locator));
            this.AcknowledgeNo = frame.AcknowledgeNo;
            this.SequenceNo = frame.SequenceNo;
            this.AddressFamily = frame.AddressFamily;
            this.Destination = frame.Destination;
            this.Source = frame.Source;
            this.Ttl = frame.Ttl;
            this.Aborted = false;
            this.Estableshed = false;
            this.SendBufferSize = frame.WindowSize;
            this.ReceiveBufferSize = frame.WindowSize;
            this.InputStream = new TcpInputStream(this);
            this.InputStream.Receive += (sender, e) =>
            {
                uint ackNo = Convert.ToUInt32(e.SequenceNo + e.Payload.Length);
                uint seqNo = e.AcknowledgeNo;
                this.SequenceNo = ackNo;
                {
                    this.Post(TcpFlags.TCP_ACK, ackNo, seqNo, 0);
                }
                this.OnMessage(e.Payload);
            };
        }

        internal virtual void Post(TcpFlags flags, uint ackno, uint seqno, uint length = 0, int retransmission = 0, bool timeout = false)
        {
            SegmentsContext segments = null;
            lock (this)
            {
                lock (this.SegmentsContexts)
                {
                    segments = new SegmentsContext()
                    {
                        AcknowledgeNo = ackno,
                        SequenceNo = seqno,
                        Flags = flags,
                        Length = length,
                        Stopwatch = new Stopwatch(),
                        Retransmission = retransmission,
                        Pcb = this,
                        Timeout = timeout,
                    };
                    if (retransmission > 0)
                    {
                        long nackNo = segments.SequenceNo + segments.Length;
                        if (this.SegmentsContexts.TryAdd(nackNo, segments))
                        {
                            segments.Stopwatch.Start();
                        }
                    }
                }
            }
            TcpFrame frame = segments.CreateFrame(this);
            this.Locator.Tcp.Output(frame);
        }

        internal virtual bool Ack(long ackno)
        {
            bool ack = false;
            SegmentsContext segments = null;
            lock (this.SegmentsContexts)
            {
                ack = this.SegmentsContexts.Remove(ackno, out segments);
                if (ack)
                {
                    segments.Stopwatch.Stop();
                    UpdateAckTime(segments.Stopwatch.ElapsedMilliseconds);

                    if (this.State == TcpState.SYN_RCVD)
                    {
                        this.State = TcpState.ESTABLISHED;
                        this.OnOpen(EventArgs.Empty);
                    }
                }

                if (this.SegmentsContexts.Count > 0 && ackno <= this.AcknowledgeNo)
                {
                    var rapids = new List<long>();
                    foreach (var key in this.SegmentsContexts.Keys)
                    {
                        if (key > ackno)
                        {
                            break;
                        }

                        rapids.Add(key);
                    }
                    foreach (var key in rapids)
                    {
                        this.SegmentsContexts.Remove(key, out segments);
                    }
                }
            }
            return ack;
        }

        private void UpdateAckTime(long rtt)
        {
            RTT = Convert.ToInt64(rtt);
            if (0 == SRTT)
            {
                SRTT = RTT;
                RTTVAL = RTT / 2;
            }
            else
            {

                RTTVAL = ((RTTVAL * 3) + Math.Abs(RTT - SRTT)) / 4;
                SRTT = (7 * SRTT + RTT) / 8;
                SRTT = SRTT <= 0 ? 1 : SRTT;
            }
            RTO = SRTT + Math.Min(1, RTTVAL * 4);
            if (RTO < MIN_RTO)
            {
                RTO = MIN_RTO;
            }
        }

        public override string ToString()
        {
            return $"{this.Source} <-> {this.Destination}";
        }

        public override bool Send(BufferSegment payload)
        {
            if (payload == null || payload.Length <= 0)
            {
                return false;
            }

            bool sendto(BufferSegment buffer)
            {
                if (buffer == null || buffer.Length <= 0)
                {
                    return false;
                }

                SegmentsContext segments = null;
                lock (this)
                {
                    lock (this.SegmentsContexts)
                    {
                        segments = new SegmentsContext()
                        {
                            AcknowledgeNo = this.SequenceNo,
                            SequenceNo = this.AcknowledgeNo,
                            Flags = TcpFlags.TCP_PSH | TcpFlags.TCP_ACK,
                            Length = (uint)buffer.Length,
                            Stopwatch = new Stopwatch(),
                            Pcb = this,
                            Retransmission = 5,
                            Payload = payload,
                        };
                        var ackNo = segments.SequenceNo + segments.Length;
                        if (this.SegmentsContexts.TryAdd(ackNo, segments))
                        {
                            segments.Stopwatch.Start();
                            this.AcknowledgeNo += segments.Length;
                        }
                    }
                }

                TcpFrame frame = segments.CreateFrame(this);
                this.Locator.Tcp.Output(frame);

                return true;
            }

            foreach (BufferSegment buffer in Slices(payload))
            {
                if (!sendto(buffer))
                {
                    return false;
                }
            }
            return true;
        }

        private void CloseOrTimeout(bool timeout = false)
        {
            var pcb = this;
            lock (this)
            {
                if (!this.Aborted)
                {
                    uint seqno = pcb.AcknowledgeNo++;
                    uint ackno = pcb.SequenceNo;
                    if (timeout)
                    {
                        pcb.Post(TcpFlags.TCP_FIN | TcpFlags.TCP_ACK, ackno, seqno);
                    }
                    else
                    {
                        pcb.Post(TcpFlags.TCP_FIN | TcpFlags.TCP_ACK, ackno, seqno, 1, 3);
                    }
                    this.Aborted = false;
                    this.OnAbort(EventArgs.Empty);
                }
            }
        }

        public virtual void Timeout()
        {
            CloseOrTimeout(true);
        }

        public override void Close()
        {
            CloseOrTimeout();
        }
    }
}

ICMP

namespace Ppp.Net.Icmp
{
    using System;
    using System.Net;
    using System.Net.Sockets;
    using Ppp.Core;
    using Ppp.Net.IP;

    public enum IcmpType : byte
    {
        ICMP_ER = 0,        /* echo reply */
        ICMP_DUR = 3,       /* destination unreachable */
        ICMP_SQ = 4,        /* source quench */
        ICMP_RD = 5,        /* redirect */
        ICMP_ECHO = 8,      /* echo */
        ICMP_TE = 11,       /* time exceeded */
        ICMP_PP = 12,       /* parameter problem */
        ICMP_TS = 13,       /* timestamp */
        ICMP_TSR = 14,      /* timestamp reply */
        ICMP_IRQ = 15,      /* information request */
        ICMP_IR = 16,       /* information reply */
        ICMP_AM = 17,       /* address mask request */
        ICMP_AMR = 18,      /* address mask reply */
    }

    public class IcmpFrame
    {
        public virtual IcmpType Type { get; set; }

        public virtual byte Code { get; set; }

        public virtual ushort Identification { get; set; }

        public virtual ushort Sequence { get; set; }

        public virtual IPAddress Source { get; }

        public virtual IPAddress Destination { get; }

        public virtual int Ttl { get; set; }

        public virtual AddressFamily AddressFamily { get; }

        public virtual BufferSegment Payload { get; set; }

        public IcmpFrame(IPAddress source, IPAddress destination, BufferSegment payload)
        {
            this.Ttl = IPFrame.DefaultTtl;
            this.Payload = payload ?? new BufferSegment(BufferSegment.Empty);
            this.Source = source ?? throw new ArgumentNullException(nameof(source));
            this.Destination = destination ?? throw new ArgumentNullException(nameof(Destination));
            this.AddressFamily = destination.AddressFamily;
        }

        public override string ToString()
        {
            return string.Format($"{Source} -> {Destination}");
        }
    }
}
namespace Ppp.Net.Icmp
{
    using System;
    using System.Net.Sockets;
    using System.Runtime.InteropServices;
    using Ppp.Core;
    using Ppp.Net.Entry;
    using Ppp.Net.IP;

    public unsafe class IcmpLayer
    {
        [StructLayout(LayoutKind.Sequential, Pack = 1)] // RFC 792(http://www.faqs.org/rfcs/rfc792.html)
        struct icmp_hdr
        {
            public byte icmp_type;      // icmp service type, 8 echo request, 0 echo reply
            public byte icmp_code;      // icmp header code
            public ushort icmp_chksum;  // icmp header chksum
            public ushort icmp_id;      // icmp packet identification
            public ushort icmp_seq;     // icmp packet sequent
        }

        public virtual ILayerLocator Locator { get; }

        public IcmpLayer(ILayerLocator locator)
        {
            this.Locator = locator ?? throw new ArgumentNullException(nameof(locator));
        }

        public virtual void Input(IcmpFrame frame)
        {

        }

        public virtual void Output(IcmpFrame frame)
        {
            IPFrame ip = ToIPFrame(frame);
            if (ip != null)
            {
                Locator.IPv4.Output(ip);
            }
        }

        public static IPFrame ToIPFrame(IcmpFrame frame)
        {
            if (frame == null)
            {
                throw new ArgumentNullException(nameof(frame));
            }
            if (frame.AddressFamily != AddressFamily.InterNetwork)
            {
                throw new ArgumentNullException("ICMP frames of this address family type are not supported.");
            }
            BufferSegment payload = frame.Payload;
            byte[] buffer = new byte[sizeof(icmp_hdr) + payload?.Length ?? 0];
            fixed (byte* pinned = buffer)
            {
                icmp_hdr* icmp = (icmp_hdr*)pinned;
                icmp->icmp_type = (byte)frame.Type;
                icmp->icmp_code = frame.Code;
                icmp->icmp_id = CheckSum.ntohs(frame.Identification);
                icmp->icmp_seq = CheckSum.ntohs(frame.Sequence);
                icmp->icmp_chksum = 0;
                if (payload != null)
                {
                    Marshal.Copy(payload.Buffer, payload.Offset, (IntPtr)(pinned + sizeof(icmp_hdr)), payload.Length);
                }
                icmp->icmp_chksum = CheckSum.inet_chksum(icmp, buffer.Length);
                if (icmp->icmp_chksum == 0)
                {
                    icmp->icmp_chksum = 0xffff;
                }
            }
            return new IPFrame(ProtocolType.Icmp, frame.Source, frame.Destination, new BufferSegment(buffer))
            {
                Ttl = frame.Ttl,
            };
        }

        public static IcmpFrame ParseFrame(IPFrame ip, bool checksum = true)
        {
            if (ip == null)
            {
                return null;
            }
            IcmpFrame frame = null;
            BufferSegment segment = ip.Payload;
            segment.UnsafeAddrOfPinnedArrayElement(p =>
            {
                icmp_hdr* icmp = (icmp_hdr*)p;
                if (checksum && icmp->icmp_chksum != 0)
                {
                    ushort cksum = CheckSum.inet_chksum(icmp, segment.Length);
                    if (cksum != 0)
                    {
                        return;
                    }
                }
                int payload_size = segment.Length - sizeof(icmp_hdr);
                if (payload_size < 0)
                {
                    return;
                }
                frame = new IcmpFrame(ip.Source, ip.Destination, new BufferSegment(segment.Buffer, segment.Offset + sizeof(icmp_hdr), payload_size))
                {
                    Type = (IcmpType)icmp->icmp_type,
                    Code = icmp->icmp_code,
                    Identification = CheckSum.ntohs(icmp->icmp_id),
                    Sequence = CheckSum.ntohs(icmp->icmp_seq),
                    Ttl = ip.Ttl,
                };
            });
            return frame;
        }

        public virtual IcmpFrame Parse(IPFrame ip) => ParseFrame(ip);
    }
}

IPv4

namespace Ppp.Net.IP
{
    using System;
    using System.Net;
    using System.Net.Sockets;
    using Ppp.Core;

    public enum IPFlags : ushort
    {
        IP_RF = 0x8000,        /* reserved fragment flag */
        IP_DF = 0x4000,        /* dont fragment flag */
        IP_MF = 0x2000,        /* more fragments flag */
        IP_OFFMASK = 0x1fff,   /* mask for fragmenting bits */
    }

    public unsafe class IPFrame : EventArgs
    {
        public const int DefaultTtl = 64;

        public IPFrame(ProtocolType protocolType, IPAddress source, IPAddress destination, BufferSegment payload)
        {
            this.Destination = destination ?? throw new ArgumentNullException(nameof(destination));
            this.Source = source ?? throw new ArgumentNullException(nameof(source));
            this.AddressFamily = destination.AddressFamily;
            if (source.AddressFamily != destination.AddressFamily)
            {
                throw new ArgumentOutOfRangeException("The original address is inconsistent with the target address protocol.");
            }
            this.Ttl = DefaultTtl;
            this.Tos = IPv4Layer.TOS_ROUTIN_MODE;
            this.Flags = IPFlags.IP_DF;
            this.ProtocolType = protocolType;
            this.Payload = payload ?? throw new ArgumentNullException(nameof(payload));
        }

        public virtual AddressFamily AddressFamily { get; }

        public virtual ushort Id { get; set; }

        public virtual IPFlags Flags { get; set; }

        public virtual int FragmentOffset
        {
            get
            {
                int offset = (ushort)this.Flags;
                offset = ((ushort)(offset << 3)) >> 3;
                offset <<= 3;
                return offset;
            }
            set
            {
                int flags = (int)this.Flags >> 13;
                flags = flags << 13 | value >> 3;
                this.Flags = (IPFlags)flags;
            }
        }

        public virtual IPAddress Source { get; set; }

        public virtual IPAddress Destination { get; set; }

        public static uint GetAddressV4(IPAddress address)
        {
            if (address == null || address.AddressFamily != AddressFamily.InterNetwork)
            {
                return 0;
            }

            byte[] addressBytes = address.GetAddressBytes();
            fixed (byte* p = addressBytes)
            {
                if (p == null)
                {
                    return 0;
                }

                return *(uint*)p;
            }
        }

        public virtual uint SourceAddressV4
        {
            get
            {
                return GetAddressV4(this.Source);
            }
        }

        public virtual uint DestinationAddressV4
        {
            get
            {
                return GetAddressV4(this.Destination);
            }
        }

        public virtual BufferSegment Payload { get; }

        public virtual BufferSegment Options { get; set; }

        public virtual int Ttl { get; set; }

        public virtual byte Tos { get; set; }

        public virtual ProtocolType ProtocolType { get; }

        public virtual bool HasFlag(IPFlags flags)
        {
            ushort v = CheckSum.ntohs((ushort)this.Flags);
            return (v & CheckSum.ntohs((ushort)(flags))) != 0;
        }

        public override string ToString()
        {
            return string.Format($"{this.Source} -> {this.Destination}");
        }
    }
}
namespace Ppp.Net.IP
{
    using Ppp.Core;
    using Ppp.Net.Icmp;
    using Ppp.Net.Tcp;
    using Ppp.Net.Udp;

    public interface IPLayer
    {
        TcpLayer Tcp { get; }

        UdpLayer Udp { get; }

        IcmpLayer Icmp { get; }

        void Output(IPFrame frame);

        void Input(IPFrame frame);

        IPFrame Parse(BufferSegment buffer);
    }
}
namespace Ppp.Net.IP
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Net;
    using System.Net.Sockets;
    using System.Runtime.InteropServices;
    using System.Threading;
    using Ppp.Core;
    using Ppp.Net.Entry;
    using Ppp.Net.Icmp;
    using Ppp.Net.Tcp;
    using Ppp.Net.Udp;

    public unsafe class IPv4Layer : IPLayer
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private static volatile int _locationId = Environment.TickCount;

        public const byte TOS_ROUTIN_MODE = 0x00;

        public virtual TcpLayer Tcp { get; }

        public virtual UdpLayer Udp { get; }

        public virtual IcmpLayer Icmp { get; }

        public virtual ILayerLocator Locator { get; }

        public IPv4Layer(ILayerLocator locator)
        {
            this.Locator = locator ?? throw new ArgumentNullException(nameof(locator));
            this.Tcp = locator.Tcp;
            this.Udp = locator.Udp;
            this.Icmp = locator.Icmp;
        }

        public virtual void Output(IPFrame frame)
        {
            var packet = ToArray(frame);
            if (packet != null)
            {
                Locator.Netif.Output(packet);
            }
        }

        public virtual void Input(IPFrame frame)
        {
            if (frame.ProtocolType == ProtocolType.Tcp)
            {
                var f = Tcp.Parse(frame);
                if (f != null)
                {
                    Tcp.Input(f);
                }
            }
            else if (frame.ProtocolType == ProtocolType.Udp)
            {
                var f = Udp.Parse(frame);
                if (f != null)
                {
                    Udp.Input(f);
                }
            }
            else if (frame.ProtocolType == ProtocolType.Icmp)
            {
                var f = Icmp.Parse(frame);
                if (f != null)
                {
                    Icmp.Input(f);
                }
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 20)]
        private struct ip_hdr
        {
#pragma warning disable 0649
            /* version / header length / type of service */
            public byte _v_hl;
            /* type of service */
            public byte _tos;
            /* total length */
            public ushort _len;
            /* identification */
            public ushort _id;
            /* fragment offset field */
            public ushort _flags;
            /* time to live */
            public byte _ttl;
            /* protocol */
            public byte _proto;
            /* checksum */
            public ushort _chksum;
            /* source and destination IP addresses */
            public uint src;
            public uint dest;
#pragma warning restore 0649

            public static int IPH_V(ip_hdr* hdr)
            {
                return ((hdr)->_v_hl >> 4);
            }

            public static int IPH_HL(ip_hdr* hdr)
            {
                return ((hdr)->_v_hl & 0x0f);
            }

            public static int IPH_PROTO(ip_hdr* hdr)
            {
                return ((hdr)->_proto & 0xff);
            }

            public static int IPH_OFFSET(ip_hdr* hdr)
            {
                return (hdr)->_flags;
            }

            public static int IPH_TTL(ip_hdr* hdr)
            {
                return ((hdr)->_ttl & 0xff);
            }
        }

        private const uint IP_ADDR_ANY_VALUE = 0x00000000;
        private const uint IP_ADDR_BROADCAST_VALUE = 0xffffffff;

        private static bool ip_addr_isbroadcast(uint addr)
        {
            /* all ones (broadcast) or all zeroes (old skool broadcast) */
            if ((~addr == IP_ADDR_ANY_VALUE) ||
                (addr == IP_ADDR_ANY_VALUE))
                return true;
            return false;
        }

        public const int IP_PROTO_ICMP = 1;
        public const int IP_PROTO_UDP = 17;
        public const int IP_PROTO_TCP = 6;
        public const int IP_PROTO_IGMP = 2;
        public const int IP_PROTO_GRE = 47;
        private const int IP_HLEN = 20;

        public virtual IPFrame Parse(BufferSegment buffer) => ParseFrame(buffer);

        public static IPFrame ParseFrame(BufferSegment packet, bool checksum = true)
        {
            if (packet == null)
            {
                return null;
            }
            IPFrame frame = null;
            packet.UnsafeAddrOfPinnedArrayElement((payload) =>
            {
                ip_hdr* iphdr = (ip_hdr*)payload;
                if (iphdr == null)
                {
                    return;
                }
                if (ip_hdr.IPH_V(iphdr) != 4)
                {
                    return;
                }
                int iphdr_hlen = ip_hdr.IPH_HL(iphdr) << 2;
                if (iphdr_hlen > packet.Length)
                {
                    return;
                }
                if (iphdr_hlen < IP_HLEN)
                {
                    return;
                }
                int ttl = ip_hdr.IPH_TTL(iphdr);
                if (ttl <= 0)
                {
                    return;
                }
                if (checksum && iphdr->_chksum != 0)
                {
                    int cksum = CheckSum.inet_chksum(iphdr, iphdr_hlen);
                    if (cksum != 0)
                    {
                        return;
                    }
                }
                if (ip_addr_isbroadcast(iphdr->src) || ip_addr_isbroadcast(iphdr->dest)) 
                {
                    return;
                }
                if ((ip_hdr.IPH_OFFSET(iphdr) & CheckSum.ntohs((ushort)(IPFlags.IP_OFFMASK | IPFlags.IP_MF))) != 0) // 不允许IP分片(NAT不太容易处理好分片)
                {
                    return;
                }
                ProtocolType protocolType = (ProtocolType)ip_hdr.IPH_PROTO(iphdr);
                if (protocolType == (ProtocolType)IP_PROTO_UDP ||
                    protocolType == (ProtocolType)IP_PROTO_TCP ||
                    protocolType == (ProtocolType)IP_PROTO_ICMP ||
                    protocolType == (ProtocolType)IP_PROTO_GRE)
                {
                    BufferSegment message_data = new BufferSegment(packet.Buffer, 
                        packet.Offset + iphdr_hlen, 
                        packet.Length - iphdr_hlen);
                    BufferSegment options_data = null;
                    int options_size = (iphdr_hlen - sizeof(ip_hdr));
                    if (options_size <= 0)
                    {
                        options_data = new BufferSegment(BufferSegment.Empty);
                    }
                    else
                    {
                        options_data = new BufferSegment(packet.Buffer,
                                packet.Offset + sizeof(ip_hdr), options_size);
                    }
                    frame = new IPFrame(protocolType,
                        new IPAddress(iphdr->src),
                        new IPAddress(iphdr->dest),
                        message_data)
                    {
                        Id = CheckSum.ntohs(iphdr->_id),
                        Ttl = ttl,
                        Tos = iphdr->_tos,
                        Options = options_data,
                        Flags = (IPFlags)CheckSum.ntohs(iphdr->_flags),
                    };
                }
            });
            return frame;
        }

        public static ushort NewId() => (ushort)Interlocked.Increment(ref _locationId);

        public static IList<IPFrame> Subpackages(IPFrame frame)
        {
            if (frame == null)
            {
                throw new ArgumentNullException(nameof(frame));
            }
            List<IPFrame> subpackages = new List<IPFrame>();
            if (0 != (frame.Flags & IPFlags.IP_MF))
            {
                subpackages.Add(frame);
                return subpackages;
            }
            BufferSegment messages = frame.Payload;
            BufferSegment options = frame.Options;
            if (messages == null)
            {
                subpackages.Add(frame);
                return subpackages;
            }
            int max = Tap.MTU - sizeof(ip_hdr);
            if (options != null)
            {
                max -= options.Length;
            }
            max = unchecked((max >> 3) << 3);
            int szz = messages.Length;
            int ofs = 0;
            IPFrame fragment = null;
            while (szz > max)
            {
                fragment = new IPFrame(frame.ProtocolType,
                    frame.Source,
                    frame.Destination,
                    new BufferSegment(messages.Buffer, messages.Offset + ofs, max))
                {
                    Flags = IPFlags.IP_MF,
                    Id = frame.Id,
                    Options = frame.Options,
                    Ttl = frame.Ttl,
                    Tos = frame.Tos,
                    FragmentOffset = ofs,
                };
                ofs += max;
                szz -= max;
                subpackages.Add(fragment);
            }
            fragment = new IPFrame(frame.ProtocolType,
                frame.Source,
                frame.Destination,
                new BufferSegment(messages.Buffer, messages.Offset + ofs, szz))
            {
                Flags = subpackages.Count <= 0 ? frame.Flags : 0,
                Id = frame.Id,
                Options = frame.Options,
                Ttl = frame.Ttl,
                Tos = frame.Tos,
                FragmentOffset = ofs,
            };
            subpackages.Add(fragment);
            return subpackages;
        }

        public static BufferSegment ToArray(IPFrame frame)
        {
            if (frame == null)
            {
                throw new ArgumentNullException(nameof(frame));
            }
            BufferSegment payload_segment = frame.Payload;
            BufferSegment options_segment = frame.Options;
            int options_size = 0;
            if (options_segment != null)
            {
                options_size = options_segment.Length;
            }
            int payload_offset = sizeof(ip_hdr) + options_size;
            int payload_size = 0;
            if (payload_segment != null)
            {
                payload_size = payload_segment.Length;
            }
            byte[] message_data = new byte[payload_offset + payload_size];
            fixed (byte* pinned = message_data)
            {
                ip_hdr* iphdr = (ip_hdr*)pinned;
                iphdr->dest = frame.DestinationAddressV4;
                iphdr->src = frame.SourceAddressV4;
                iphdr->_ttl = (byte)frame.Ttl;
                iphdr->_proto = (byte)frame.ProtocolType;
                iphdr->_v_hl = (byte)(4 << 4 | payload_offset >> 2);
                iphdr->_tos = frame.Tos; // Routine Mode
                iphdr->_len = CheckSum.htons((ushort)message_data.Length);
                iphdr->_id = CheckSum.htons(frame.Id);
                iphdr->_flags = CheckSum.ntohs((ushort)(frame.Flags == 0 ? IPFlags.IP_DF : frame.Flags));
                iphdr->_chksum = 0;

                if (options_size > 0)
                {
                    IntPtr destination_options = (IntPtr)(pinned + sizeof(ip_hdr));
                    Marshal.Copy(options_segment.Buffer, options_segment.Offset, destination_options, options_size);
                }

                if (payload_size > 0)
                {
                    Buffer.BlockCopy(payload_segment.Buffer, payload_segment.Offset, message_data, payload_offset, payload_size);
                }

                iphdr->_chksum = CheckSum.inet_chksum(pinned, payload_offset);
                if (iphdr->_chksum == 0)
                {
                    iphdr->_chksum = 0xffff;
                }
            }
            return new BufferSegment(message_data);
        }
    }
}

CheckSum

namespace Ppp.Core
{
    public unsafe static class CheckSum
    {
        public static ushort ip_standard_chksum(void* dataptr, int len)
        {
            uint acc;
            ushort src;
            byte* octetptr;

            acc = 0;
            /* dataptr may be at odd or even addresses */
            octetptr = (byte*)dataptr;
            while (len > 1)
            {
                /* declare first octet as most significant
                   thus assume network order, ignoring host order */
                src = (ushort)((*octetptr) << 8);
                octetptr++;
                /* declare second octet as least significant */
                src |= (*octetptr);
                octetptr++;
                acc += src;
                len -= 2;
            }
            if (len > 0)
            {
                /* accumulate remaining octet */
                src = (ushort)((*octetptr) << 8);
                acc += src;
            }
            /* add deferred carry bits */
            acc = (uint)((acc >> 16) + (acc & 0x0000ffffUL));
            if ((acc & 0xffff0000UL) != 0)
            {
                acc = (uint)((acc >> 16) + (acc & 0x0000ffffUL));
            }
            /* This maybe a little confusing: reorder sum using htons()
               instead of ntohs() since it has a little less call overhead.
               The caller must invert bits for Internet sum ! */
            return ntohs((ushort)acc);
        }

        public static ushort inet_chksum(void* dataptr, int len)
        {
            return (ushort)~ip_standard_chksum(dataptr, len);
        }

        private static uint FOLD_U32T(uint u)
        {
            return ((uint)(((u) >> 16) + ((u) & 0x0000ffffUL)));
        }

        private static uint SWAP_BYTES_IN_WORD(uint w)
        {
            return (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8);
        }

        public static ushort ntohs(ushort n)
        {
            ushort r = 0;
            byte* p1 = (byte*)&n;
            byte* p2 = (byte*)&r;

            p2[0] = p1[1];
            p2[1] = p1[0];
            return r;
        }

        public static uint ntohl(uint n)
        {
            uint r = 0;
            byte* p1 = (byte*)&n;
            byte* p2 = (byte*)&r;

            p2[0] = p1[3];
            p2[1] = p1[2];
            p2[2] = p1[1];
            p2[3] = p1[0];
            return r;
        }

        public static uint htonl(uint n)
        {
            return ntohl(n);
        }

        public static ushort htons(ushort n)
        {
            return ntohs(n);
        }

        public static ushort inet_cksum_pseudo_base(byte* payload, uint proto, uint proto_len, uint acc)
        {
            bool swapped = false;
            acc += ip_standard_chksum(payload, (int)proto_len);
            acc = FOLD_U32T(acc);

            if (proto_len % 2 != 0)
            {
                swapped = !swapped;
                acc = SWAP_BYTES_IN_WORD(acc);
            }

            if (swapped)
            {
                acc = SWAP_BYTES_IN_WORD(acc);
            }

            acc += htons((ushort)proto);
            acc += htons((ushort)proto_len);

            acc = FOLD_U32T(acc);
            acc = FOLD_U32T(acc);

            return (ushort)~(acc & 0xffffUL);
        }

        public static ushort inet_chksum_pseudo(byte* payload, uint proto, uint proto_len, uint src, uint dest)
        {
            uint acc;
            uint addr;

            addr = src;
            acc = (addr & 0xffff);
            acc = (acc + ((addr >> 16) & 0xffff));
            addr = dest;
            acc = (acc + (addr & 0xffff));
            acc = (acc + ((addr >> 16) & 0xffff));
            /* fold down to 16 bits */
            acc = FOLD_U32T(acc);
            acc = FOLD_U32T(acc);

            return inet_cksum_pseudo_base(payload, proto, proto_len, acc);
        }
    }
}

BufferSegment

namespace Ppp.Core
{
    using System;
    using System.IO;
    using System.Text;
    using System.Collections;
    using System.Linq;
    using System.Collections.Generic;
#if NET40
    using System.Collections.Concurrent;
#endif

    public unsafe class BufferSegment : EventArgs, IEnumerable<byte>
    {
        public new static readonly byte[] Empty = new byte[0];
        public static readonly IntPtr Null = IntPtr.Zero;

        public virtual byte[] Buffer { get; private set; }

        public virtual int Offset { get; private set; }

        public virtual int Length { get; private set; }

        public BufferSegment(byte[] buffer) : this(buffer, buffer?.Length ?? 0)
        {

        }

        public BufferSegment(byte[] buffer, int length) : this(buffer, 0, length)
        {

        }

        public BufferSegment(byte[] buffer, int offset, int length)
        {
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }
            this.Buffer = buffer ?? Empty;
            if ((offset + length) > buffer.Length)
            {
                throw new ArgumentOutOfRangeException("The offset and length overflow the size of the buffer.");
            }
            this.Offset = offset;
            this.Length = length;
        }

        ~BufferSegment()
        {
            this.Buffer = null;
            this.Offset = 0;
            this.Length = 0;
            GC.SuppressFinalize(this);
        }

        public virtual BufferSegment Depth()
        {
            byte[] b = null;
            int length = this.Length;
            if (length <= 0)
            {
                b = BufferSegment.Empty;
            }
            else
            {
                b = new byte[length];
            }
            BufferSegment s = new BufferSegment(b);
            if (b.Length > 0)
            {
                this.CopyTo(s.Buffer);
            }
            return s;
        }

        public static implicit operator BufferSegment(byte[] b)
        {
            if (b == null)
            {
                return new BufferSegment(Empty);
            }
            return new BufferSegment(b);
        }

        public virtual ArraySegment<byte> ToArraySegment()
        {
            return new ArraySegment<byte>(this.Buffer, this.Offset, this.Length);
        }

        public virtual byte[] ToArray()
        {
            return ToArraySegment().ToArray();
        }

        public void CopyTo(byte[] destination)
        {
            ToArraySegment().CopyTo(destination, 0);
        }

        public virtual void CopyTo(byte[] destination, int destinationIndex)
        {
            ToArraySegment().CopyTo(destination, destinationIndex);
        }

        public virtual void CopyTo(Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            stream.Write(this.Buffer, this.Offset, this.Length);
        }

        public IntPtr UnsafeAddrOfPinnedArrayElement()
        {
            return UnsafeAddrOfPinnedArrayElement(null);
        }

        public virtual IntPtr UnsafeAddrOfPinnedArrayElement(Action<IntPtr> callback)
        {
            IntPtr ptr = IntPtr.Zero;
            var buffer = this.Buffer;
            fixed (byte* pinned = buffer)
            {
                if (pinned != null)
                {
                    int num = (this.Offset + this.Length);
                    if (buffer.Length >= num)
                    {
                        ptr = (IntPtr)(pinned + this.Offset);
                    }
                }
                callback?.Invoke(ptr);
            }
            return ptr;
        }

        public override string ToString()
        {
            string s = string.Empty;
            UnsafeAddrOfPinnedArrayElement((p) =>
            {
                if (p == null)
                    s = null;
                else
                    s = new string((sbyte*)p, 0, this.Length, Encoding.Default);
            });
            return s;
        }

        public virtual IEnumerator GetEnumerator()
        {
            IEnumerable<byte> enumerator = this;
            return enumerator.GetEnumerator();
        }

        IEnumerator<byte> IEnumerable<byte>.GetEnumerator()
        {
            var offset = this.Offset;
            var size = this.Length;
            var pinned = this.Buffer;
            if (offset > 0 && size > 0)
            {
                for (int i = offset; i < size; i++)
                {
                    yield return pinned[i];
                }
            }
        }
    }

    public unsafe static class Extension
    {
        public static int IndexOf(ref int[] next, byte* src, int src_len, byte* sub, int sub_len)
        {
            int i = 0;
            int j = 0;
            FindNext(ref next, sub, sub_len);
            while (i < src_len && j < sub_len)
            {
                if (j == -1 || src[i] == sub[j])
                {
                    i++;
                    j++;
                }
                else
                {
                    j = next[j];
                }
            }
            if (j >= sub_len)
            {
                return i - sub_len;
            }
            else
            {
                return -1;
            }
        }

        private static void FindNext(ref int[] next, byte* sub, int sub_len)
        {
            int l = sub_len - 1;
            int i = 0;
            int j = -1;
            next[0] = -1;
            while (i < l)
            {
                if (j == -1 || sub[i] == sub[j])
                {
                    j++;
                    i++;
                    if (sub[i] == sub[j])
                    {
                        next[i] = next[j];
                    }
                    else
                    {
                        next[i] = j;
                    }
                }
                else
                {
                    j = next[j];
                }
            }
        }

#if !NETCOREAPP2_0
        public static T[] ToArray<T>(this ArraySegment<T> segment)
        {
            T[] s = null;
            if (segment != null)
            {
                s = new T[segment.Count];
                CopyTo(segment, s, 0);
            }
            return s;
        }

        public static void CopyTo<T>(this ArraySegment<T> segment, T[] destination, int destinationIndex)
        {
            if (segment != null && destination != null && destinationIndex >= 0)
            {
                Buffer.BlockCopy(segment.Array, segment.Offset, destination, destinationIndex, segment.Count);
            }
        }

        public static bool Remove<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, out TValue value)
        {
            value = default(TValue);
            if (dictionary == null)
            {
                return false;
            }

            lock (dictionary)
            {
                if (!dictionary.TryGetValue(key, out value))
                {
                    return false;
                }

                return dictionary.Remove(key);
            }
        }

#if !NET40
        private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
        {
            using (IEnumerator<TFirst> e3 = first.GetEnumerator())
            {
                using (IEnumerator<TSecond> e2 = second.GetEnumerator())
                {
                    while (e3.MoveNext() && e2.MoveNext())
                    {
                        yield return resultSelector(e3.Current, e2.Current);
                    }
                }
            }
        }

        public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
        {
            if (first == null)
            {
                throw new ArgumentNullException(nameof(first));
            }
            if (second == null)
            {
                throw new ArgumentNullException(nameof(second));
            }
            if (resultSelector == null)
            {
                throw new ArgumentNullException(nameof(resultSelector));
            }
            return ZipIterator(first, second, resultSelector);
        }
#endif

        public static IEnumerable<KeyValuePair<TKey, TValue>> ToList<TKey, TValue>(this IDictionary<TKey, TValue> d)
        {
#if NET40
            if (d is ConcurrentDictionary<TKey, TValue>)
            {
                return d;
            }
#endif
            if (d is global::Ppp.Collections.ConcurrentDictionary<TKey, TValue>)
            {
                return d;
            }
            return Enumerable.ToList(d);
        }

        public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
        {
            if (dictionary == null)
            {
                return false;
            }

            lock (dictionary)
            {
                if (dictionary.ContainsKey(key))
                {
                    return false;
                }

                dictionary.Add(key, value);
            }
            return true;
        }
#endif
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值