DHCP 协议分析及实现 - Ⅰ

背景

最近因工作需要接触了 H3C 的 iNode 客户端, 参考其官网对 iNode 的描述:

iNode智能客户端是H3C公司自行设计开发的基于Windows的多业务接入客户端软件,提供802.1x、Portal、VPN等多种认证方式,可以与H3C以太网交换机、路由器、VPN网关等网络设备共同组网,实现对局域网、广域网、VPN、无线接入的用户认证,是对用户终端进行身份验证、安全状态评估以及安全策略实施的主体,可以按照企业接入安全策略的要求,实现基于角色/身份的权限和安全控制。

以此为发端, 本来主要讨论 DHCP 协议的设计与实现, 以期最后通过修改 DHCP 协议实现 私有 的 DHCP 协议, 实现一定条件下限制网络访问的目的.

同时亦声明本文仅就技术上讨论一种私有协议实现的可能性, 只有如何实现以及是否可行并不在本文讨论范围内.

[]

解决 MikroTik RouterOS 下 NAT 回环问题

背景

前段时间通过客服拿到了联通的公网 IP, 因此也申请了域名并做了解析, 也顺利应当地在路由器上做了端口映射, 从而可以在外网可以直接通过域名加端口的形式访问内网的服务. 但也有一个问题就是当处于内网环境时通过域名反而无法访问同一内网下的服务, 必须使用内网 IP 才行.

其实这个问题很明显, 就是网络报文在通过路由器时路由器将报文转发给了内网服务器, 但是内网服务器在回报文的时候直接按内网地址回包了. 这个问题由来已久, 也即 NAT 回环问题. 以下我们将通过脚本的形式在 RouterOS 上添加 NAT 规则来解决这个 NAT 回环问题.

具体环境为光猫配置了桥接后通过 MikroTik RB750Gr3 进行拨号, 同时 RB750Gr3 运行的 RouterOS 版本为 7.7, 并且内网网段为 192.168.2.0/24.

[]

在 CentOS 8 下编译 QEMU 以支持运行 macOS 虚拟机

背景

由于项目组选择采用 Electron 构建跨平台客户端, 因此存在一个问题就是如何利用 CI/CD 进行自动构建与发布. 当前内部使用 Jenkins 进行自动构建, 但 Jenkins 仍需要 macOS 平台以便构建 DMG 包进行分发. 因此在不考虑实体机的情况下, 最便捷的方案便是可以能创建一台 macOS 的虚拟机了, 通过虚拟机可以方便地复制构建环境从而不和任意硬件货环境绑定.

但当前的问题一是默认情况下 CentOS 8 下提供的 4.2.0 版本的 QEMU 比较老了, 另一个问题则是 RHEL 在分发的时候移除了相当多的 QEMU 特性, 无法直接创建 macOS 虚拟机, 因此我们需要手动修改 QEMU 的代码并自行构建 RPM 包修复这两个问题.

[]

Spring Boot 项目下载文件接口问题

背景

今天组内研发同学报告说项目中有一个模块提供了文件下载的接口, 在实际使用中遇到有客户使用 迅雷 进行离线下载的情况, 同时模块中针对文件下载的接口做了 AOP 切面进行计费统计. 而 迅雷 会针对该接口进行多线程并发下载, 同时即使退出 迅雷, 迅雷 还会继续发起对文件下载接口的调用. 于是便抽空大致看了下项目内的代码, 出问题的接口功能也比较简单, 并没有很复杂的逻辑. 其大致实现如下:

[]

详解Linux下内存使用及释放

最近频频遇到系统负载不高且当下也没有大量的 IO 操作,但是内存使用率却较高的情况。 因此有必要仔细研究下 Linux 下的内存管理机制,其他类 Unix 平台也多少大同小异,但是也不可一概而论,待时间充裕再比较分析其他系统下的内存管理机制。

首先 Linux 同时涉及 虚拟地址空间 和 物理内存区域 两个层次。同时 用户空间与内核空间则属于虚拟地址空间的划分,而在物理上又被分为三块:ZONE_DMA,ZONE_NORMAL 和ZONE_HIGH。这里的 ZONE_DMA 是可直接存取访问的内存区域,即不需要任何的逻辑映射,大小为 0~16MiB;ZONE_NORAML 为内核所保留使用的,其大小为16~896MiB;剩下的 896~End 都是 ZONE_HIGH 的区域,该段区域是应用程序进行 malloc() 时所能访问的地址(当然应用程序可以通过系统调用来访问 ZONE_NORMAL 区域)。说完内存的映射,那么就应该讲解操作系统如何管理内存了——**内存的分配与回收:**内存的分配在 kernel 中是通过 Buddy 算法(即伙伴算法)来做统一分配的,伙伴系统能够在一定程度上缓解外部碎片,并通过伙伴合并提高连续物理内存分配的成功率,从而在很大程度上提高了内存的使用效率(但无法完全消除内存碎片);内存回收则是通过 LRU 算法,即基于最近最少使用算法来进行回收的。LRU 算法的核心思想是:如果资源已较长时间未使用了,那么在最近的某段时间内,该资源用到的可能性也几乎是不可能的。同时内核维护着两张链表,分别为 Active 和 Inactive 链表,当进程不再活动或是分配的内存已不再使用时,内存将有原 Active 链表移到 Inactive 链表尾部,当操作系统发现有必要进行内存回收时,将由 Inactive 链表表头开始,从而保证所回收的内存地址空间确实是不再使用并且可以进行回收的。

MMU_principle

2000px-Virtual_memory

那么知道内存的划分与分配回收算法,剩下的就是内核具体如何分配和回收的问题了。内存的管理在 kernel 中是 VM(Virtual Memory)子系统承担的。VM 主要做两件事:线性映射 和 动态释放。线性映射即将用户态的物理内存映射为逻辑上线性连续的内存空间,然后当应用程序申请内存时将其分配给应用,而申请操作是由应用程序自身发起的,常见的即 malloc();动态释放即内存的回收,内存回收在内核中由线程 kswapd 完成。kswapd 是 Linux 内核创建的后台内核线程,负责在内存压力达到一定条件时执行后台内存平衡和回收。其具体实例和作用范围与内存节点、内核配置等有关,其虽然没有独立的内存空间但也占用非常少量的内存,kswapd 的运行方式有两种:内核主动调用 和 时钟源中断。时钟中断即每若干时钟滴答后,kswapd 即由 wakeup_kswapd 进行唤醒;第二种方式是当可用内存低于一定的水位后,由内核主动发生调用。如下图所示:

Zone_Watermarks

那么具体地,这些水位的值该如何计算呢?核心代码其实在 /mm/page_alloc.c 中,我们来简单地看下(暂时只关心 ZONE_HIGHMEM部分,因为这部分才是和我们的应用程序息息相关的):

[]