从零开始做一个SDWAN

VPN和SD-WAN的区别

VPN(Virtual Private Network)和SD-WAN(Software-Defined Wide Area Network)是两种不同的网络技术,它们在目的、功能和实施方式上有一些重要的区别。
目的和应用场景:

VPN:VPN主要用于在公共网络上创建一个加密的隧道,以确保数据的安全传输。它通常用于连接远程办公人员或远程站点到公司内部网络,或者在云服务提供商和企业之间建立安全的连接。VPN的主要目的是提供隐私和安全,将数据包加密,以保护数据在传输过程中不被窃取或篡改。

SD-WAN:SD-WAN是一种广域网(WAN)技术,旨在改善广域网络的性能、可用性和管理。它通过智能路由、负载均衡、应用程序优化和连接多样性等功能来提供更高的网络性能和可用性。SD-WAN的主要目的是优化网络流量,提高带宽利用率,并降低网络延迟,以改善应用程序性能。

网络拓扑

VPN:VPN通常建立在点对点或点对多点拓扑上,将远程站点或用户连接到中央网络。它的重点是连接性和隐私。所有数据流量需要通过VPN服务器中转。

SD-WAN:SD-WAN通常建立在分布式拓扑上,涵盖多个地理位置和连接类型。它的重点是性能优化和带宽管理。当需要访问另一个机房的服务时才通过互联网转发流量到机房里,数据流量通过P2P通信,不从服务器中转。

安全性

VPN:VPN强调加密和隐私,确保数据在传输过程中受到保护。它通常使用安全协议如IPsec或SSL/TLS来建立安全连接。

SD-WAN:SD-WAN通常也包括加密和安全功能,但其主要焦点在于性能优化和负载均衡。一些SD-WAN解决方案可能会结合VPN以提供完整的网络安全性。

应用程序优化

VPN:VPN通常不提供主动的应用程序性能优化。它只负责数据的加密和传输。

SD-WAN:SD-WAN通常具有应用程序识别和优化功能,可以识别和加速关键应用程序,从而提供更好的性能。

管理和可见性

VPN:VPN的管理相对简单,通常涉及到连接的配置和用户的身份验证。它可能不提供广泛的网络性能和可见性分析。

SD-WAN:SD-WAN通常提供更高级的网络管理和监控工具,以优化网络性能、监控连接和应用程序的使用情况,并进行故障排除。
综上所述,VPN和SD-WAN是两种不同的网络技术,它们的重点和功能不同。

VPN:是单隧道,一次只能连接一个机房,正常上网的流量也要走VPN服务器端发出。

SD-WAN:是运行在本机的软路由,可以同时连接多个机房,并且正常上网走本机的流量。

TUN设备


Linux TUN(Tunnel)设备是一种虚拟网络设备,它用于创建用户态程序和内核之间的通信通道,以便用户态程序可以处理网络数据包。TUN设备通常用于创建VPN(Virtual Private Network)和其他网络隧道,以实现网络隔离、数据加密和路由功能。

TUN设备(Tunnel设备)工作在网络协议栈的第三层,也就是网络层。具体来说,它是一个虚拟网络设备,负责处理和传输网络层(IP层)数据包。 TUN设备主要用于创建用户态程序和内核之间的通信通道,以便用户态程序可以读取和处理网络层数据包。

以下是一些关于Linux TUN设备的重要信息和用法:

虚拟设备:TUN设备是一种虚拟网络设备,类似于物理网络接口,但没有对应的硬件。它被创建为一个字符设备文件,通常是 /dev/net/tun。

数据包传输:TUN设备用于用户态程序和内核之间的数据包传输。用户态程序可以通过TUN设备读取和写入IP数据包。

隧道和VPN:TUN设备通常用于创建网络隧道,例如VPN。通过TUN设备,用户态VPN客户端程序可以将数据包封装并发送
到网络隧道,然后内核将其路由到网络中。

数据包处理:用户态程序可以使用TUN设备来处理数据包,例如加密、解密、路由、过滤等操作。这使得TUN设备成为实现网络安全和隔离的有力工具。

从上图可以更直观的看出TUN设备和物理设备的区别:虽然它们的一端都是连着网络协议栈,但是物理网卡另一端连接的是物理网络,而TUN设备另一端连接的是一个应用层程序,这样协议栈发送给TUN的数据包就可以被这个应用程序读取到,此时这个应用程序可以对数据包进行一些自定义的修改(比如封装成 UDP),然后又通过网络协议栈发送出去——其实

这就是目前大多数VPN程序的工作原理。

Windows也是支持TUN设备的以wintun.dll的方式调用。

JNI与JNA

Java JNI(Java Native Interface)和Java JNA(Java Native Access)都是用于在Java应用程序中与本地(原生)库进行交互的工具,但它们在实现和使用上有一些重要的区别:

实现方式:

JNI:Java JNI是一种Java平台的标准,它允许Java代码通过本地方法调用来与本地库进行交互。JNI需要在Java代码和本地C/C++代码之间编写本地方法接口(JNI接口),并将Java对象转换为本地数据类型。

JNA:Java JNA是一个独立的Java库,它使用Java的反射机制和动态链接来实现与本地库的交互。与JNI不同,JNA无需编写本地方法接口,而是通过Java接口和注解来描述与本地函数的映射。

复杂度:

JNI:使用JNI需要编写本地方法接口(JNI接口),这可能会增加代码的复杂性和维护成本。JNI还需要手动管理内存分配和释放,因此容易引入内存泄漏和错误。

JNA:JNA的使用相对简单,无需编写本地方法接口,而且自动处理内存管理。这使得JNA更容易学习和使用,尤其对于不熟悉C/C++的Java开发者来说更友好。

性能:

JNI:JNI通常具有更高的性能,因为它直接与本地代码进行交互,无需额外的中间层。然而,JNI的性能也取决于编写的本地代码的质量。

JNA:JNA的性能可能会略低于JNI,因为它需要进行Java到本地库之间的数据类型转换和调用。对于某些对性能要求非常高的应用程序,JNI可能更适合。

跨平台性:

JNI:由于JNI依赖于本地C/C++代码,因此需要为每个目标平台编写不同的本地实现。这使得跨平台开发更复杂。

JNA:JNA具有更好的跨平台性,因为它本身是纯Java实现,不需要为不同平台编写不同的本地代码。JNA库会处理底层平台差异。

开发效率:

JNI:虽然JNI可以提供更高的性能,但在开发效率方面可能不如JNA,因为它需要编写额外的本地接口和手动管理内存。

JNA:JNA在开发效率方面更有优势,因为它无需编写本地接口,更容易上手,尤其适合快速原型开发和跨平台应用程序。

JNA调用wintun

https://www.wintun.net/

private static class WinTun {

    static {
        try {
            Native.register(WinTun.class, "wintun");
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public static final int ERROR_NO_MORE_ITEMS = 259;
    public static final int WINTUN_MIN_RING_CAPACITY = 0x20000;
    public static final int WINTUN_MAX_RING_CAPACITY = 0x4000000;

    public static native Pointer WintunCreateAdapter(WString Name, WString TunnelType, String RequestedGUID) throws LastErrorException;

    public static native void WintunCloseAdapter(Pointer Adapter) throws LastErrorException;

    public static native Pointer WintunStartSession(Pointer Adapter, int Capacity) throws LastErrorException;

    public static native void WintunEndSession(Pointer Session) throws LastErrorException;

    public static native Pointer WintunReceivePacket(Pointer Session, Pointer PacketSize) throws LastErrorException;

    public static native void WintunReleaseReceivePacket(Pointer Session, Pointer Packet) throws LastErrorException;

    public static native Pointer WintunAllocateSendPacket(Pointer Session, long PacketSize) throws LastErrorException;

    public static native void WintunSendPacket(Pointer Session, Pointer Packet) throws LastErrorException;

    public static native Pointer WintunGetReadWaitEvent(Pointer Session) throws LastErrorException;

    public static native void WintunGetAdapterLUID(Pointer Adapter, Pointer Luid) throws LastErrorException;

    public static native int WintunGetRunningDriverVersion() throws LastErrorException;
}
public void test() {
    Pointer adapter = WintunCreateAdapter(new WString("tun"), new WString("sdwan"), UUID.randomUUID().toString());
    Pointer session = WintunStartSession(adapter, WINTUN_MAX_RING_CAPACITY);
    while (true) {
        //receive
        Pointer packetSizePointer = new Memory(Native.POINTER_SIZE);
        Pointer packetPointer = WintunReceivePacket(session, packetSizePointer);
        try {
            int packetSize = packetSizePointer.getInt(0);
            byte[] bytes = packetPointer.getByteArray(0, packetSize);
            ByteBuf byteBuf = Unpooled.buffer(bytes.length);
            byteBuf.writeBytes(bytes);
            
            //todo process
        	......
            //send
            Pointer packetPointer = WintunAllocateSendPacket(session, bytes.length);
            packetPointer.write(0, bytes, 0, bytes.length);
            WintunSendPacket(session, packetPointer);
        } finally {
            WintunReleaseReceivePacket(session, packetPointer);
        }
    }
}

SD-WAN工作原理


在这里插入图片描述

名词解释

Controller:
负责给Node的虚拟IP分配、向Node推送其他Node信息和配置管理。

Node:
每个上线的主机为一个Node节点,可以直接连接同一个SD-WAN controller下的所有节点,或访问Mesh下的主机。

Mesh:
Mesh也是Node的一个,通常部署在Linux服务器上。当一个局域网下部署了Mesh后,这个局域网下的主机都可以通过Mesh被访问到。

STUN:
node一般是家用电脑没有公网IP,node与node之间不能直接通信。需要通过STUN服务发现公网IP和端口用于P2P通信。

IPV4协议解析

从TUN设备上读取到的都是IP数据包,业务上使用需要对IPv4协议进行解析

https://www.rfc-editor.org/rfc/rfc791.txt

IPv4数据报文是IPv4协议中用于在计算机网络中传输数据的基本单位。它是IPv4网络通信中的核心部分,包含了网络通信所需的信息,例如源IP地址、目标IP地址、协议、数据负载等。以下是一个典型的IPv4数据报文的结构:

IPv4数据报文的主要字段包括:

版本(Version):4位字段,指示协议版本,IPv4的版本为4。

头部长度(Header Length):4位字段,指示IPv4头部的长度,以32位字(4字节)为单位。通常情况下,IPv4头部长度为20字节,但它可以变化,取决于选项字段的存在。

区分服务(Differentiated Services,DS):8位字段,用于指定数据报文的服务质量和优先级。通常被称为“服务类型”(Type of Service)字段,用于实现服务质量(QoS)。

总长度(Total Length):16位字段,指示整个IPv4数据报文的总长度,包括头部和数据。最大总长度为65535字节。

标识(Identification):16位字段,用于唯一标识分片的数据报文,通常在分片时使用。

标志(Flags):3位字段,用于控制数据报文的分片和重组过程。包括“不分片”、“更多分片”和“分片偏移”。

分片偏移(Fragment Offset):13位字段,指示分片在原始数据报文中的位置。

生存时间(Time to Live,TTL):8位字段,表示数据报文在网络中可以经过多少跃点(路由器)后被丢弃。TTL字段用于防止数据报文在网络中无限循环。

协议(Protocol):8位字段,指示数据报文中封装的上层协议,例如TCP、UDP、ICMP等。

首部校验和(Header Checksum):16位字段,用于检测IPv4头部的错误。校验和字段用于验证头部信息的完整性。

源IP地址(Source IP Address):32位字段,指示数据报文的发送者的IP地址。

目标IP地址(Destination IP Address):32位字段,指示数据报文的接收者的IP地址。

选项(Options):可选字段,用于指定附加的信息和控制选项,例如记录路由、时间戳等。选项字段的存在和长度可以变化。

数据(Data):可选字段,包含传输的实际数据,长度和内容根据协议和应用程序的需要而变化。

  0                   1                   2                   3   
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |Version|  IHL  |Type of Service|          Total Length         |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |         Identification        |Flags|      Fragment Offset    |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |  Time to Live |    Protocol   |         Header Checksum       |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                       Source Address                          |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                    Destination Address                        |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                    Options                    |    Padding    |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

image.png

public void decode(ByteBuf byteBuf) {
    short head = byteBuf.readUnsignedByte();
    short version = (byte) (head >> 4);
    byte headLen = (byte) ((head & 0b00001111) * 4);
    short diffServices = byteBuf.readUnsignedByte();
    int totalLen = byteBuf.readUnsignedShort();
    int id = byteBuf.readUnsignedShort();
    int flags = byteBuf.readUnsignedShort();
    short liveTime = byteBuf.readUnsignedByte();
    int protocol = byteBuf.readUnsignedByte();
    int checksum = byteBuf.readUnsignedShort();
    byte[] srcIPBytes = new byte[4];
    byteBuf.readBytes(srcIPBytes);
    String srcIP = bytes2IP(srcIPBytes);
    byte[] dstIPBytes = new byte[4];
    byteBuf.readBytes(dstIPBytes);
    String dstIP = bytes2IP(dstIPBytes);
    ByteBuf payload = byteBuf.readSlice(byteBuf.readableBytes());
}

public String bytes2IP(byte[] bytes) {
    int d1 = bytes[0] & 0b11111111;
    int d2 = bytes[1] & 0b11111111;
    int d3 = bytes[2] & 0b11111111;
    int d4 = bytes[3] & 0b11111111;
    String ip = String.format("%s.%s.%s.%s", d1, d2, d3, d4);
    return ip;
}

TUN设备是一个三层设备,从/dev/net/tun字符设备上读取的是IP数据包,写入的也只能是IP数据包。
SourceAddress为本机的IP地址,如果是TUN设备上读取到的为预先配置在tun设备上的虚拟IP。
DestinationAddress为数据包要发送到的目标IP,需要询问Controller这个IP是哪个Node或是Mesh可以路由的,当路由时还需要修改源IP和目标IP,并重新计算checksum

六、IPv4 checksum代码分析

public int calcChecksum() {
    ByteBuf byteBuf = Unpooled.buffer();
    byte head = (byte) ((version << 4) | (headerLen / 4));
    byteBuf.writeByte(head);
    byteBuf.writeByte(diffServices);
    byteBuf.writeShort(totalLen);
    byteBuf.writeShort(id);
    byteBuf.writeShort(flags);
    byteBuf.writeByte(liveTime);
    byteBuf.writeByte(protocol);
    //checksum字段置为0
    byteBuf.writeShort(0);
    byteBuf.writeBytes(ip2bytes(srcIP));
    byteBuf.writeBytes(ip2bytes(dstIP));
    //数据长度为奇数,在该字节之后补一个字节
    if (0 != byteBuf.readableBytes() % 2) {
        byteBuf.writeByte(0);
    }
    int sum = 0;
    while (byteBuf.readableBytes() > 0) {
        sum += byteBuf.readUnsignedShort();
    }
    int h = sum >> 16;
    int l = sum & 0b11111111_11111111;
    sum = (h + l);
    sum = 0b11111111_11111111 & ~sum;
    return sum;
}

public static byte[] ip2bytes(String ip) {
    String[] split = ip.split("\\.");
    byte[] bytes = new byte[split.length];
    for (int i = 0; i < split.length; i++) {
        bytes[i] = (byte) Integer.parseInt(split[i]);
    }
    return bytes;
}

STUN协议解析

https://www.rfc-editor.org/rfc/rfc5389.txt

STUN(Session Traversal Utilities for NAT)协议的RFC 5389规范是用于处理网络地址转换(NAT)和防火墙穿越的协议标准。STUN协议的主要目标是帮助在NAT或防火墙后面的设备(例如,VoIP电话、视频会议终端等)发现其公共IP地址和端口,以便建立对等连接或进行其他网络通信。
NAT穿越:STUN协议允许设备发现自己在NAT或防火墙后面的情况,并获取其在公共Internet上的可用IP地址和端口。这对于建立对等连接和进行点对点通信非常重要。

UDP协议:STUN协议基于UDP(User Datagram Protocol)工作,通常使用3478端口。UDP是一种面向无连接的协议,适用于实时通信应用程序。
请求-响应模式:STUN使用请求-响应模式,客户端发送STUN请求到STUN服务器,服务器将响应返回给客户端,响应包含客户端的公共IP地址和端口。
Keep-Alive:STUN协议还支持保持活动状态的功能,以确保NAT映射不会过期并导致通信中断。这对于长时间持续通信非常重要。
RFC 5389协议扩展:RFC 5389定义了STUN协议的基本规范,但STUN也支持多种扩展和变体,用于更复杂的网络配置和需求。这些扩展可以包括TURN(Traversal Using Relays around NAT)和ICE(Interactive Connectivity Establishment)等。
总之,STUN协议是一种用于解决NAT和防火墙问题的协议,它允许设备发现其公共IP地址和端口,并帮助建立对等连接和实时通信。STUN协议在实现多种实时通信应用程序和服务时发挥着重要作用,如VoIP、视频会议、在线游戏等。

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0|     STUN Message Type     |         Message Length        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Magic Cookie                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                     Transaction ID (96 bits)                  |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
public static final byte[] Cookie = new byte[]{(byte) 0x21, (byte) 0x12, (byte) 0xa4, (byte) 0x42};

public void decode(ByteBuf byteBuf) {
    int type = byteBuf.readUnsignedShort();
    int len = byteBuf.readUnsignedShort();
    //cookie
    byteBuf.skipBytes(Cookie.length);
    byte[] tranIdBytes = new byte[12];
    byteBuf.readBytes(tranIdBytes);
    String tranId = new String(tranIdBytes);
    ByteBuf attrs = byteBuf.readSlice(len);
    while (attrs.readableBytes() > 0) {
        int t = attrs.readUnsignedShort();
        int l = attrs.readUnsignedShort();
        ByteBuf v = attrs.readSlice(l);
    }
}

MTU

MTU(Maximum Transmission Unit,最大传输单元)是一个计算机网络术语,用于描述可以在网络中以单个数据包的形式传输的最大数据量。它通常以字节为单位表示。MTU是网络链路或设备所支持的最大数据包大小,超过这个大小的数据包将被分片(分成更小的片段)以适应链路的MTU限制。

链路和网络设备:不同的网络链路和设备(如以太网、Wi-Fi、DSL、3G/4G网络等)具有不同的MTU限制。MTU通常由网络硬件或操作系统配置来设置。

数据包分片:如果发送的数据包大于链路或设备的MTU限制,网络设备将对其进行分片,将数据包分成适合MTU的片段。接收端设备会重新组装这些分片以还原原始数据包。

通信效率:通常情况下,更大的MTU可以提高网络通信的效率,因为较小的数据包需要更多的网络开销和处理。然而,如果数据包超过链路的MTU,将会导致分片和重新组装,可能会引入一些延迟和性能损失。
Path MTU Discovery(PMTUD):为了避免数据包在网络中被分片,IPv4和IPv6协议支持Path MTU Discovery。这是一种机制,通过在网络中发送探测数据包并监听ICMP错误消息,以动态确定路径上的最小MTU,从而确保发送的数据包不会被分片。

调整MTU:在某些情况下,网络管理员或用户可能需要调整设备的MTU设置,以适应特定网络或应用程序的需求。例如,在使用VPN或封装协议时,可能需要增加MTU以容纳额外的头部信息。

总之,MTU是网络中一个重要的参数,它定义了可以在网络中传输的最大数据包大小。了解和管理MTU对于确保网络通信的可靠性和性能非常重要。在网络故障排除和性能优化时,MTU是一个需要考虑的关键因素。

应用层可用字节数计算

一个典型的MTU数据包包括:数据链路层头部+网络层头部+传输层头部+应用层数据

数据链路层头部(Ethernet头部):14字节

网络层头部(IPv4头部):20字节

传输层头部(通常是TCP或UDP头部):20字节(TCP)或8字节(UDP)

应用层数据:取决于应用程序生成的数据
这些头部大小是一般情况下的标准值,但可能会因特殊情况而变化。总的MTU大小通常为 1500 字节(Ethernet常见的MTU大小),但这个值可以根据网络设备和链路的配置而有所不同。

留给SD-WAN传输的应用层数据大概在1440字节左右。

CIDR

CIDR(Classless Inter-Domain Routing)是一种用于 IP 地址分配和路由的方法,它取代了传统的子网掩码和分类的 IP 地址分配方案。CIDR 引入了更灵活的方式来表示 IP 地址和网络,使得网络规划和管理更加高效和精确。
无类别路由:CIDR 是"无类别路由"的缩写,这意味着它不再受限于传统的A、B、C类网络分类。传统的分类方式将 IP 地址分为不同的类别,每个类别具有固定的网络和主机部分。CIDR 不再依赖于这些固定的类别,而是允许更灵活地划分 IP 地址空间。

CIDR 表示法:CIDR 使用一种称为CIDR表示法的格式来表示 IP 地址和子网掩码。这种表示法将 IP 地址后面跟着斜杠和一个前缀长度(如192.168.1.0/24),前缀长度表示网络部分的位数。例如,/24 表示前24位是网络部分,剩下的8位是主机部分。

精确的子网划分:CIDR 允许更精确地划分 IP 地址空间,而不受传统类别的限制。这意味着网络管理员可以根据实际需求创建不同大小的子网,更有效地使用 IP 地址。

IPv6 支持:CIDR 不仅适用于IPv4地址,还适用于IPv6地址。IPv6地址空间非常庞大,CIDR表示法在IPv6中的应用尤其重要。

超网和聚合:CIDR还支持超网(Supernetting)和聚合(Aggregation)的概念。超网是将多个连续的 IP 地址块合并成一个更大的地址块,而聚合是将多个较小的地址块汇总成一个更大的块,以减少路由表的条目数量,提高路由效率。

路由表优化:CIDR 的引入有助于减小因路由表过于庞大而导致的路由器内存和处理器负载问题。通过使用CIDR表示法,可以更紧凑地表示大量的网络和地址。

总的来说,CIDR 是一种灵活和高效的 IP 地址分配和路由方案,它允许网络管理员更精确地规划网络,减小路由表的规模,提高网络的性能和可管理性。它是现代互联网中广泛使用的技术之一。

以192.168.1.0/24为例计算地址池

找出共享前缀部分:在这个范围内,共享的前缀部分是192.168.1.0。这是网络标识部分。

计算前缀长度:共享前缀部分有24位,所以前缀长度为24。

确定子网掩码:前缀长度为24的子网掩码是由24个1位和8个0位组成的二进制数,即255.255.255.0。以下是二进制表示:

确定可用IP地址范围

将192.168.1.0转数字3232235776

计算 3232235776 + Math.pow(2, 32 - 24) - 1 = 3232236031

3232236031 转IP地址为 192.168.1.255

11000000_10101000_00000001_00000000 (192.168.1.0)
11000000_10101000_00000001_11111111 (192.168.1.255)
11111111_11111111_11111111_00000000 (255.255.255.0)

结论

起始IP:192.168.1.0

结束IP:192.168.1.255

子网掩码:255.255.255.0

子网数量:256

public static int ip2int(String ip) {
    String[] split = ip.split("\\.");
    int s = 0;
    int bit = 24;
    for (String sp : split) {
        int n = Integer.parseInt(sp) << bit;
        s |= n;
        bit -= 8;
    }
    return s;
}

public static String int2ip(int s) {
    int d1 = s >> 24 & 0b11111111;
    int d2 = s >> 16 & 0b11111111;
    int d3 = s >> 8 & 0b11111111;
    int d4 = s & 0b11111111;
    String ip = String.format("%s.%s.%s.%s", d1, d2, d3, d4);
    return ip;
}

controller分配虚拟IP

每一个node向controller注册后,controller根据配置的CIDR地址池中分配一个虚拟IP,这个过程类似DHCP。node与controller之间通过TCP通信,当node离线时分配的虚拟IP会被自动回收重用,当node1需要访问node2的时候,就是直接访问node2的虚拟IP。

当然也可以像路由器一样给node分配一个静态的虚拟IP,controller会为node保持这个IP,不再分配给别的node以防止IP冲突的问题。

Mesh

一般的node只能和其他的node之间相互通信,但是不能像VPN一样访问node所在局域网内的其他主机服务。这时候需要使用到支持mesh的node,mesh在向controller注册时,会解析当前主机分配的内网IP、掩码计算CIDR向controller注册。
以node本地IP为172.168.1.2为例子,需要PING企业内部的主机192.168.1.5

1.node1在tun0网卡上读取到PING 192.168.1.5的请求

2.node1向controller发起nodeARP命令询问192.168.1.5的IP数据包需要发送到哪个node/mesh

3.controller应答发送到mesh1并返回mesh1的公网映射地址

4.node1封装IP数据包成UDP数据包通过eth0发送到mesh1

5.mesh1从eth0读取UDP数据包

6.mesh1解析IP数据包,写入tun0网卡

7.tun0数据包走eth0发出

8.内网主机接收到PING请求

Windows路由

windows要想192.168.1.5能被访问通,还需要配置route。默认情况下192.168.1.5会从windows的本地网络(eth0)发送出去。我们需要配置windows route使数据包被tun0网卡接收。

route add 192.168.1.5/32 10.1.0.2
route print -4
活动路由:
网络目标      网络掩码            网关            接口              跃点数
0.0.0.0      0.0.0.0           172.168.1.1     172.168.1.2      5
192.168.1.5  255.255.255.255   在链路上         10.1.0.2         6

UDP打洞分析

STUN RFC 5389中定义了一种用于在NAT(Network Address Translator)和防火墙后面的设备之间建立UDP连接的协议。在RFC 5389中,“mapping” 和 “filtering” 是两个重要的概念,用于描述STUN协议的工作方式。

Mapping(映射):
Mapping 是指将内部(私有)IP地址和端口映射到外部(公共)IP地址和端口的过程。当设备位于NAT后面时,它的内部IP地址和端口在传输到互联网上时会被NAT设备替换为外部IP地址和端口。这个映射允许外部设备向内部设备发送数据包,同时NAT设备可以将传入的数据包正确路由到内部设备。
STUN协议中的"Binding Request"和"Binding Response"消息用于发现和创建这种映射关系。设备可以向STUN服务器发送"Binding Request",并从服务器接收"Binding Response"来获取其映射关系。

Filtering(过滤):
Filtering 涉及NAT设备的过滤规则,用于确定哪些数据包可以通过NAT并进入内部网络。NAT设备通常会实施一些安全策略,以限制外部流量进入内部网络。这些策略可能包括端口过滤、IP地址过滤等。
STUN协议中的"Binding Request"和"Binding Response"消息通常允许这些消息通过NAT设备,因为它们是协助建立映射关系的关键消息。如果NAT设备将它们过滤掉,STUN协议将无法正常工作。

Mapping规则:

a. Endpoint-Independent Mapping(EIM):这是最宽松的映射规则。它意味着当设备在内部网络上建立了一个映射关系后,不仅可以接收来自相同目标IP地址和端口的数据包,还可以接收来自任何IP地址和端口的数据包。这意味着映射是基于设备的,而不是特定的通信对。

b. Address-Dependent Mapping(ADM):这种映射规则更为限制。它要求来自相同目标IP地址的数据包必须使用相同的映射,但允许来自不同目标IP地址的数据包使用不同的映射。这个规则比EIM更严格,但仍然相对宽松。

c. Address and Port-Dependent Mapping(APDM):这是最严格的映射规则。它要求来自相同目标IP地址和端口的数据包必须使用相同的映射,不允许不同的通信对使用相同的映射。这种规则非常严格,通常由对网络安全性要求非常高的NAT设备实施。

Filtering规则:

a. Endpoint-Independent Filtering(EIF):这是最宽松的过滤规则。它允许通过NAT的数据包不仅可以来自相同的源IP地址和端口,还可以来自任何源IP地址和端口。这种规则非常宽松,通常用于允许STUN协议消息通过NAT。

b. Address-Dependent Filtering(ADF):这种过滤规则要求来自相同源IP地址的数据包必须使用相同的映射。但不同源IP地址的数据包可以使用不同的映射。这比EIF规则要求更严格。

c. Address and Port-Dependent Filtering(APDF):这是最严格的过滤规则。它要求来自相同源IP地址和端口的数据包必须使用相同的映射,不允许不同源IP地址和端口的数据包使用相同的映射。这种规则非常严格,通常由要求高度安全性的NAT设备实施。
总之,STUN协议允许不同级别的映射和过滤规则,具体取决于NAT设备的实现和安全要求。不同的规则级别提供了不同程度的灵活性和安全性,以满足各种网络环境的需求。

Linux转发配置

在上述例子步骤7还是有问题的,由于TUN设备是三层的虚拟网卡,不具备二层的数据链路层的转发功能,因此需要配置tun0的数据包由eth0负责转发。

vi /etc/sysctl.conf
net.ipv4.ip_forward = 1

net.ipv4.ip_forward = 0:
这是默认设置,表示禁用IPv4数据包的转发。
当此参数的值为0时,Linux内核不会将接收到的IPv4数据包重新路由到其他网络接口。这意味着Linux系统仅接受本地目标的数据包,并不具备路由功能。

net.ipv4.ip_forward = 1:
这个设置启用了IPv4数据包的转发。
当此参数的值为1时,Linux内核将根据路由表信息,将接收到的IPv4数据包转发到适当的网络接口,以便它们能够到达其目标。这是典型的路由器或网络关口设备的配置。

iptables -A FORWARD -i tun0 -o eth0 -j ACCEPT

这条iptables命令的含义是将数据包从网络接口tun0进入,并允许它们通过网络接口eth0出去。具体解释如下:

-A FORWARD:这部分指示iptables将规则添加到"FORWARD"链中。"FORWARD"链是负责转发在Linux系统上通过的数据包的链。

-i tun0:这部分指定了数据包的进入接口。这意味着这个规则将应用于从tun0接口进入的数据包。

-o eth0:这部分指定了数据包的出口接口。这意味着这个规则将应用于通过eth0接口出去的数据包。

-j ACCEPT:这部分指定了规则的动作。具体地,它表示允许数据包通过。当数据包匹配这个规则时,它将被允许通过FORWARD链,进入tun0接口并离开eth0接口。

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

这条iptables命令的含义是将数据包进行网络地址转换(NAT),并使用MASQUERADE方式处理它们。具体解释如下:

-t nat:这部分指示iptables将规则添加到"nat"表中。"nat"表用于进行网络地址转换。

-A POSTROUTING:这部分指示规则将应用于数据包在离开系统之后的阶段。在此情况下,它将应用于数据包在"POSTROUTING"阶段,即在数据包离开系统之前。

-o eth0:这部分指定了数据包的出口接口。这意味着这个规则将应用于通过eth0接口出去的数据包。

-j MASQUERADE:这部分指定了规则的动作。具体地,它表示使用MASQUERADE方式处理匹配的数据包。MASQUERADE是一种网络地址转换(NAT)技术,通常用于将局域网内部的私有IP地址映射到公共IP地址上,以便这些数据包可以在公共网络上正常传输。

数据包通过eth0接口出去之前,对它们进行NAT处理,具体来说是使用MASQUERADE方式。这通常用于在局域网内部使用私有IP地址的情况下,将数据包路由到互联网上,并确保它们能够正确返回到局域网内部,以实现共享Internet连接的功能。

起名

原理和技术上我们都已经梳理清楚了,那我们给我们的SD-WAN起了个名字了。程序世界大家都喜欢用水果或是动物来给自己的项目起名字。
那我们的项目也一样,叫做netThunder。

组件依赖

coding…

https://github.com/jaspercloud/js-sdwan

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/103606.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是有效种草!900万播放成B站“网红品牌”

- 导语 众所周知&#xff0c;B站早期是二次元小众社区&#xff0c;所以ACG&#xff08;动画、漫画、游戏三者的合称&#xff09;内容一直是B站的核心品类&#xff0c;B站经过多品类内容的摸索和发展&#xff0c;一直到现在成为年轻一代聚集的多元化社区&#xff0c;但ACG内容仍…

AI智能监控平台EasyCVR+无人机方案:实时全景无死角全方面助力山区安防系统新升级

无人机作为高速发展的高科技设备&#xff0c;技术日趋成熟&#xff0c;目前已经被广泛应用于农业、森林防护、医疗、交通物流等等场景。但随着城市化进程的加快&#xff0c;人类对自然环境日益严重的破坏&#xff0c;近十几年来&#xff0c;森林火灾的发生频率和失火严重程度一…

Zabbix出现 404Not FoundThe requested URL /zabbix was not found on this server.

目录 一、问题&#xff1a; 二、原因&#xff1a; 三、解决方法&#xff1a; 一、问题&#xff1a; Not Found The requested URL /zabbix was not found on this server. 二、原因&#xff1a; 未找到 在此服务器上找不到请求的 URL /zabbix。 /etc/httpd/conf.d 目录…

【uniapp】小程序开发7:自定义组件、自动注册组件

一、自定义轮播图组件、自动注册 以首页轮播图组件为例。 1、创建组件文件src/components/my-swipper.vue 代码如下&#xff1a; <template><view><view class"uni-margin-wrap"><swiper class"swiper" circular :indicator-dots…

22 行为型模式-状态模式

1 状态模式介绍 2 状态模式结构 3 状态模式实现 代码示例 //抽象状态接口 public interface State {//声明抽象方法,不同具体状态类可以有不同实现void handle(Context context); }

Golang 数据库操作

文章目录 初始化连接连接池SetMaxOpenConnsSetMaxIdleConnsSetConnMaxIdleTimeSetConnMaxLifetime 查询数据插入数据更新数据删除数据实现账号密码登录功能sqlx的部分用法 首先安装包&#xff1a;Install go get -u github.com/go-sql-driver/mysql // MySQL数据库的包 go get…

单片机中的 _nop_() 延时以及其相关的基础扩展

使用 _nop_() 函数做延时遇到的一些问题 以及对此延伸出的一些需要了解的基本概念 ...... by 矜辰所致 完善文章内容结构&#xff0c;补充指令周期、机器周期等一些基本概念 2023/10/25前言 最近还是继续做着项目&#xff0c;因为在某 8051 内核芯片上使用到了 I…

【MySQL】用户与权限管理

文章目录 一、用户管理1、用户信息表2、创建用户3、删除用户4、修改用户密码 二、权限管理1、MySQL 权限2、给用户授权3、回收用户权限 一、用户管理 之前为了方便&#xff0c;我们学习 MySQL 时统一使用的都是 root 账号进行登录&#xff0c;但在实际的开发场景中必然是需要进…

opencalib中lidar2camera安装记录

目录 一、opencalib安装 二、lidar2camera的安装 三、测试运行 四、出现过的问题 一、opencalib安装 代码地址&#xff1a;https://github.com/PJLab-ADG/SensorsCalibration/blob/master/README.md # pull docker image sudo docker pull scllovewkf/opencalib:v1 # Aft…

RISC-V架构——中断委托和中断注入

1、中断委托 1.1、中断委托的作用 &#xff08;1&#xff09;默认情况下&#xff0c;所有的陷入&#xff08;中断和异常&#xff09;都是在M模式下处理&#xff0c;然后再返回到发生陷入前的模式&#xff1b; &#xff08;2&#xff09;所有陷入都在M模式处理会涉及到模式切换…

1个月5次发版:测试人的模块测试策略分类归纳

笔者所在项目经历了一个月开发周期&#xff0c;该项目有5名开发人员&#xff0c;1名项目经理&#xff0c;1名测试人员&#xff0c;涵盖OA系统8个模块&#xff0c;在短短1个月中进行了5次发布。 现进行模块测试策略分类归纳。 已有模块 配置项优化 对于已有模块的配置项优化&…

【JavaEE】线程安全的集合类 -- 多线程篇(9)

线程安全的集合类 多线程环境使用 ArrayList多线程环境使用队列多线程环境使用哈希表 多线程环境使用 ArrayList 自己使用同步机制 (synchronized 或者 ReentrantLock)Collections.synchronizedList(new ArrayList); synchronizedList 是标准库提供的一个基于 synchronized 进…

【深度学习 | Transformer】释放注意力的力量:探索深度学习中的 变形金刚,一文带你读通各个模块 —— 总结篇(三)

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

基本指令(2):通配符,重定向,命令行管道

一、通配符 rm -rf ./* # * —— 通配符&#xff0c;指定路径下的所有文件&#xff08;不包括隐藏文件&#xff09;二、重定向 在理解重定向前&#xff0c;先要有一个概念&#xff1a;Linux下一切皆文件&#xff0c;大部分硬件设备都可以看做有读写方法&#xff0c;只不过有些方…

国腾GM8775C完全替代CS5518 MIPIDSI转2 PORT LVDS

集睿致远CS5518描述&#xff1a; CS5518是一款MIPI DSI输入、LVDS输出转换芯片。MIPI DSI 支持多达4个局域网&#xff0c;每条通道以最 大 1Gbps 的速度运行。LVDS支持18位或24位像素&#xff0c;25Mhz至154Mhz&#xff0c;采用VESA或JEIDA格 式。它只能使用单个1.8v电源&am…

化工园区数字孪生可视化管控平台,赋予园区安全环保智慧发展

化工行业作为国民经济的支柱和工业发展的引擎&#xff0c;对安全生产、环保节能、应急管控有着很高的要求。目前国内外化工园区面临安全和环保两大压力。为有效解决这两大难题&#xff0c;巨蟹数科综合运用物联网、数字孪生等新一代信息技术&#xff0c;建设了数字孪生园区智慧…

Echarts多曲线数值与Y周刻度不符合

发现问题&#xff1a; 在展示多曲线图表的时候&#xff0c;发现图表曲线数值相差不大&#xff0c;但是图表展示的曲线相差很大&#xff0c;仔细观察之后发现是展示有问题(其实这并不能算是错误&#xff0c;只是由于忽略&#xff0c;导致的配置与预期不符合)。 问题复现&#x…

【分布式】大模型分布式训练入门与实践 - 04

大模型分布式训练 数据并行-Distributed Data Parallel1.1 背景1.2 PyTorch DDP1&#xff09; DDP训练流程2&#xff09;DistributedSampler3&#xff09;DataLoader: Parallelizing data loading4&#xff09;Data-parallel&#xff08;DP&#xff09;5&#xff09;DDP原理解析…

CrossOver 23.6 让Mac可以运行Windows程序的工具

在当今数字化时代&#xff0c;虚拟机技术被广泛应用于软件开发、系统测试、网络安全等领域。虚拟机提供了一个隔离的虚拟环境&#xff0c;使得我们能够在一台物理计算机上同时运行多个操作系统和应用程序。下面我们就来看虚拟机软件怎么安装&#xff0c;虚拟机怎么使用吧&#…

【Linux】TCP协议

文章目录 &#x1f4d6; 前言1. TCP协议格式2. 确认应答机制3. 16位窗口大小4. 6个标记位4.1 URG紧急指针标记位&#xff1a; 5. 超时重传机制&#xff1a;6. 连接管理机制6.1 TCP三次握手&#xff08;重点&#xff09;&#xff1a;6.1 - 1 三次握手的原因6.1 - 2 RST复位标志位…