linux_network_1
socket的创建
1 | SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) |
1 | SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) |
在linux内核中,密码相关的头文件在 include crypto下,相关概念大致有加密 块加密 异步块加密 哈希 分组加密模式等等。
算法:
AEAD算法:一种带有认证功能的加密方式。拆分为认证和加密两部分。常见的有GCM和CCM,
对称加密算法: 使用同一个密钥进行加密和解密。这意味着加密方和解密方必须事先共享同一个密钥,并且保证这个密钥的安全。
AES:AES-128 AES-256等,后个字段对应密钥长度。密钥越长,安全性能越高,加密时间越长。
DES,3DES等。
非对称加密算法: 使用一对密钥,一个公开密钥(公钥)用于加密,一个私有密钥(私钥)用于解密。公钥可以公开分享,而私钥必须保持私密。
模式:
实现不同的算法有几种模式:
主要有ECB CBC CFB OFB 和 CTR等这几种。
不同模式的区分如下:
ECB模式: ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。
CBC模式: CBC模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或。
CFB模式: 与ECB和CBC模式只能够加密块数据不同,CFB能够将块密文(Block Cipher)转换为流密文(Stream Cipher)。
OFB模式: OFB是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。
CTR模式: CTR模式是一种通过将逐次累加的计数器进行加密生成密钥流的流密码。
以aes加密算法为例。
所有的加密算法名都是xxx_alg的方式。关键成员有算法名 驱动名 算法类型 同步,异步,分组大小,上下文。加解密函数等等。
在alg结构中,块加密和普通分组加密的区别就是.cra的设置。普通分组加密指定的是cipher.同步块加密指定的是blkcipher. 异步块加密指定的是ablkcipher。
ctx:上下文。指的是算法执行过程中所要贯穿始终的数据结构。由每个算法自己定义。set_key encrypt decrypt这几个函数都可以从参数获得算法上下文的指针。算法上下文所占的内存空间由密码管理器来分配。注册alg的时候指定ctx大小和对其即可。ctx对齐对于一些硬件加密等。ctx的首地址可能需要在内存中4字节或者16字节对齐。
device mapper是linux 2.6内核中支持的逻辑卷管理的通用设备映射机制。为实现用于才能出资源管理的块设备驱动提供了一个高度模块化的内核架构。
在内核中它通过一个一个模块化的target driver实现对IO请求的过滤或者重定向。包括软raid,软加密,多路径,镜像,快照等等。device mapper用户空间相关部分主要负责配置具体的策略和控制逻辑。比如逻辑设备和哪些物理设备映射。怎么建立这些映射关系等等。而具体的过滤和重定向IO请求的工作由内核相关代码完成。整个device mapper机制由两部分组成 内核空间的device mapper驱动,用户空间的device mapper库以及他提供的dmsetup工具。
device mapper的内核相关代码在driver/md目录中。device mapper在内核中作为一个块设备驱动被注册的,包含三个重要的对象概念,mapped device,映射表,target device。
mapped device是一个逻辑抽象,是内核向外提供的逻辑设备。通过映射表描述的映射关系和target device建立映射。从mapped device 到一个 target device的映射表由一个多元组表示,该多元组由表示mappdevice逻辑的起始地址,范围,和表示在target device所在的物理设备的地址偏移量以及target类型等变量组成。以磁盘扇区为单位,512字节大小。
target device表示的是mapped device所映射的物理空间段。device mapper中这三个对象和target driver一起构成了可迭代的设备树 。
Device mapper在用户空间包括device mapper库和dmsetup工具,device mapper库就是对ioctl 用户空间创建删除devicemapper逻辑设备所需必要操作的封装。dmsetup工具是一个应用层直接操作device mapper设备的命令行工具。大致包含,发现每个mapper device相关的target device,根据配置信息创建映射表,将用户空间构建好的映射表传入内核,让内核构建mapper device对应的dm table结构。
LUKS (linux unified key setup),linux统一密钥设置。是一种高性能安全的磁盘加密方法。基于cryptsetup。使用dm-crypt作为磁盘加密后端。
定义了如何安全存储加密密钥和加密元数据。支持多重密码。最多8个key slot。不需要重加密就能够更换密码。配合dm-crypt使用。实际加密使用的是内核支持的加密算法,LUKS只负责配置,封装和密钥管理。
任何文件系统都可以加密,包括交换分区,加密卷的开头有一个未加密的头部。允许存储多达8个lusk1或者32个lusk2加密密钥,以及诸如密码类型和密钥大小之类的加密参数。
dm crypt,内核提供的磁盘加密功能。即device mapper crypto。lucks通过dmcrypt模块使用内核设备映射器子系统。负责处理设备的加密和解密。
命令行的前端,通过它来操作 dm crypt。创建和访问加密设备。
分成User Space layer 和 kernel Space layer。
在kernel space密码学算法上。主要分成软件以及硬件运算。
软件运算。主要由CPU进行密码学算法运算,不需要额外硬件,但很费CPU性能。linux kernel 原始码位于crypto subsystem下。
硬件加速。由硬件辅助进行密码学运算,不需要耗费cpu性能,但需要额外硬件。
SoC Component–许多ARM SoC厂商都会将硬件加解密元件放入SoC中,Linux Kernel原始码多位于drivers/crypto底下.且设计必须遵照Linux crypto framework,不能私下修改。
主要的功能是提供界面。让user space可存取kernel space.目前主流为cryptodev以及af_alg
不在linux kernel中自带。开源模块。需要单独移植,并挂载kernel module。
openssl支持cryptodev。通过操作cryptdev节点来操作加密
openssl从1.1开始支持af_alg。
常见的有openssl,wolfssl。
openssl提供af alg以及cryptdev的engine,可以透过engine来存取crypto api。
介绍由应用层所发出的crypto(cryptography)request,透过system call将request传送到Linux kernel端,并经由crypto subsystem将request转发给硬件算法引擎(hardware crypto engine)的流程。
Crypto subsystem是Linux系统中负责处理crypto request的子系统,除了包含流程控制机制之外,另一个重要特色就是提供算法实作的抽象层,让各家厂商能够依据需求去客制化实作方式。
其中一个常见例子就是厂商在硬件构架中加入用以加速特定算法运算效率的硬件算法引擎,并且透过crypto subsystem将驱动硬件算法引擎的流程整合进Linux系统中,供其他kernel module或是应用层使用。
在Linux系统中,想要实现应用层与硬件装置的沟通,第一个想到的就是透过character/block device driver,让应用程序开启表示此硬件装置的抽象层,并且藉由读写行为与硬件装置进行互动。
而Cryptodev-linux就是负责此角色,它提供中间层的服务,接收由应用层传送过来的crypto request,再呼叫Linux kernel crypto Subsystem的crypto API将request转发给特定的硬件算法引擎。
Cryptodev-linux为misc device类型的kernel module,预设路径是/dev/crypto,使用ioctl file operation cryptodev_ioctl来接受应用端所传递过来的数据。
应用端则是使用cryptdev.h定义好的struct crypt_op或是struct crypt_auth_op来组成指定crypto request,并呼叫ioctl system call将request送给Cryptodev-linux。
simple of cryptdev linux ioctl
另外,Cryptodev-linux也提供session机制,每个crypto request对应到一个session,而session管理当前crypto request的状态。
例如,目前session在initialized的状态,则表示此crypto request可执行encrypt,透过此方式来确保crypto request会在正确的流程下运作。
首先 crypto subsystem有两个重要元素,transformation object(tfm),被称作cipher handle,transformation implementation,是transformation object底层的实作内容,又被称为crypto algo。就是crypto engine算法实作。
之所以要区分成object和implementation,最主要的原因是有可能多个object会使用同一个implementation。
举例来说,A和B使用者都要使用hmac-sha256算法,因此会新建立A和B两个transformation object并包含A和B各自拥有的key值,但这两个object有可能会使用同一个transformation implementation来呼叫同一个crypto engine进行算法运算。
当有crypto request进来,会先根据request中指定的算法名称,从已注册的crypto algorithm list中取出适合的crypto algorithm,并新建立transformation object。
之后,transformation object会再被组成crypto subsystem所用的cipher request。cipher request有可能共享同一个transformation object,举例来说,hmac-sha256的transformation object包含了transformation implementation和一个key值,而这个transformation object可以使用在多个cipher request的messsage上进行hash算法。当cipher request完成相关设值之后,接着实际调用transformation object的transformation implementation执行算法运算。
此时会出现一个问题,就是当短时间有多个request进来时,我们该如何依序地处理request?
这点crypto subsystem也设计了方便的struct crypto_engine,crypto engine提供了queue管理机制,让多个request能够顺序地转发给对应的crypto engine。
要新增transformation implementation到crypto subsystem,最重要的就是注册transformation implementation到crypto algorithm list中。
使用crypto_register_skciphers即可完成注册。
另外,cra_priority代表各算法的优先程度,优先级高的会先被采用。
在crypto subsystem中,crypto API分成asynchronous(异步)和synchronous(同步)两种机制。
最早版本的crypto API其实只有synchronous crypto API,但随着要处理的数据量增加,运算和数据传输时间也可能大幅拉长,此时synchronous crypto API有可能让处理流程陷入较长时间的等待,因此后来引入了asynchronous crypto API,供使用者依据自己的使用场景来选择适合的机制。
而asynchronous与synchronous crypto API在命名设计上有所区别,asynchronous会在前缀多加一个a字,反之synchronous则是s字,以hash为例:
synchronous api
只要顺序的呼叫对应流程的API,并且针对返回的结果进行error handling即可。
在API的呼叫流程中,crypto_shash_update可以被多次呼叫,让使用者放入多组需要进行加密的message。当完成一组message后,可能有些中间状态是需要被保存起来的。这些状态就会存在state handler中。在使用者呼叫api前,会需要自己分配一块足够大小的内存,让crypto engine能够存放这些状态。在transformation implementation中会设定好crypto engine需所需的状态存储空间大小,使用者只需要呼叫特定API即可。
除了命名之外,由于两种机制的处理流程不同,因此所需的参数也会有所不同。
async request
包含一个callback function crypto completion,当运算完成后,会透过此callback来通知使用者继续处理完成的流程。由于asynchronous非同步机制,因此crypto engine在处理request时,行为和流程也和synchronous同步机制有蛮大的差异,其中常见的实作方式加入request queue来管理多个request,当使用者呼叫update API发送request时,则会将request加入到queue中,并直接回传处理中(-EINPROGRESS)的状态信息。
如果使用者使用asynchronous hash API,但是实际上对应的transformation implementation却是synchronous型态,crypto subsystem会主动进行相关的数据转换,因此也是可以正常运作的。
一般常见的方式是 crypto queue搭配worker,额外开一个kernel thread来与crypto engine进行沟通,让crypto request按照FIFO的顺序处理。
建立一个全局的crypto request list,将进来的request依序排到list中,建立一个worker(kernel thread)和对应的work queue来与hardware crypto engine进行沟通,worker的任务除了从crypto request list中取出request处理h之后,也可能会包含crypto engine的初始化和资源释放等工作。注册interrupt handler,当status interrupt举起时,呼叫user自定义的completion callback function来完成最后的流程。
crypto/engine.h中有提供接口可以直接调用。
hwmon,hardware monitor是linux内核中的一个子系统,用于提供对硬件设备,如温度传感器,电压监控器,风扇速度等的监控支持。该子系统允许用户通过标准的接口读取和控制硬件状态。尤其是温度,电压,风扇转速等。提供了统一的方式访问硬件监测信息。
在hwmon子系统中,主要即借助设备驱动模型中的设备的注册以及设备属性的创建,即可针对于一个硬件芯片创建多个属性文件。
hwmon子系统主要提供了接口hwmon_attr_show、hwmon_attr_store,同时抽象了针对温度芯片、风扇芯片、电源芯片等硬件监控芯片相关参数的支持,即温度芯片、风扇芯片、电源芯片等硬件监控芯片相关参数的访问接口,均会在接口hwmon_attr_show、hwmon_attr_store中被统一调用,而温度芯片、风扇芯片、电源芯片等硬件监控芯片只需要实现hwmon_ops类型函数指针即可。调用关系如下所示,其中,HWMON子系统层则为hwmon子层抽象的部分,而最下层则由具体的hwmon 设备驱动实现即可。
1 | devm_hwmon_device_register_with_groups(dev, "my_hwmon", NULL, my_temp_attr); |
1 | sensor_device_attribute |
sys/class/hwmon下暴露。
1 | cat /sys/class/hwmon/hwmon0/temp1_input # 获取温度 |
1 | void hwmon_device_unregister(struct device *dev); |
本文章介绍linux下应用层访问phy寄存器的几种方式 便于开发者开发
一些厂家会直接提供类似/dev/mdio类似的节点,可以find -name 搜索一下看看。通过操作节点可以直接操作phy寄存器
uboot下可以通过mii cmd来实现读写phy寄存器
1 | /* 成功时返回文件描述符,失败时返回-1 |
常用:
SOCK_STREAM:
流,TCP。面向连接。
SOCK_DGRAM
面向消息的,不可靠的。
1 | LONG skfd = -1; |
1 | // 内核通用框架 |
当内存发生panic的时候,需要把panic的内容以日志的方式记录下来。目前有几种方式,kdump,mtdoops,crashlog(openwrt特有),以及pstore。
kdump主要用在x86系统上,因为它使用大量的内存和硬盘信息。
mtdoops和crashlog主要用于嵌入式系统。记录文本日志。
mtdoop功能在发生oops时,把msg区写入特定的mt分区。写入过程,不支持文件系统。直接二进制文本写。 它需要由mtd驱动的支持,就是mtd驱动支持mtd_panic_write。也就是原子式写入。不能被中断。一般flash不支持。 在mtdoops.c文件,并在标准内核的drivers/mtd/mtdcore中。
1 | mtd_panic_write |
在初始化过程中,需要指定写入哪个分区,对应的分区名/号,可以写入的size是多少。使用kmsg_dump_register注册一个cxt->dump.dump绑定的回调函数 (eg:mmcoops_do_dump),此函数主要是读取kmsg区的内容,直接调用mtd->write的驱动进行写操作。
在linux内核启动的时候,保留一块64K的内存,用于记录panic日志,crashlog发生在oop时候,把msg写入之前分配好的MEM区域,并加上magic,再重启后,check magic ok,则把上次日志放入到/sys/kernel/debug/crashlog。
pstore最初是用于系统发生oops或panic时,自动保存内核log buffer中的日志。不过在当前内核版本中,其已经支持了更多的功能,如保存console日志、ftrace消息和用户空间日志。同时,它还支持将这些消息保存在不同的存储设备中,如内存、块设备或mtd设备。 为了提高灵活性和可扩展性,pstore将以上功能分别抽象为前端和后端,其中像dmesg、console等为pstore提供数据的模块称为前端,而内存设备、块设备等用于存储数据的模块称为后端,pstore core则分别为它们提供相关的注册接口。
通过模块化的设计,实现了前端和后端的解耦,因此若某些模块需要利用pstore保存信息,就可以方便地向pstore添加新的前端。而若需要将pstore数据保存到新的存储设备上,也可以通过向其添加后端设备的方式完成。
除此之外,pstore还设计了一套pstore文件系统,用于查询和操作上一次重启时已经保存的pstore数据。当该文件系统被挂载时,保存在backend中的数据将被读取到pstore fs中,并以文件的形式显示。
源码在/fs/pstore/ram_core.c
1 | fs/pstore/ |
oops/panic日志位于 pstore 目录下的dmesg-ramoops-x文件中,根据缓冲区大小可以有多个文件,x从0开始。
函数调用序列日志位于 pstore 目录下的ftrace-ramoops文件中。
1 | CONFIG_PSTORE=y |
1 | ramoops_mem: ramoops_mem { |
1 | 方案1: |
1 | mount -t pstore pstore /sys/fs/pstore |
以上几种方案都使用到了kmsg_dump的注册机制。 注册很简单,就是把一个全局变量结构挂到一个全局list中。 kmsg_dump是oops时进入kmsg_dump的入口。由panic,die,oops_exit等函数调用。它会一一调用回调函数。 每一个回调函数都会用到kmsg_dump_get_buffer。它先是计算dump还有多少空间,然后把kmsg中最后的一部分写进去。
mstar平台为例子。
ssr931g在插入usb过程中会报错。
1 | cma:cma_calloc:memory range at ptrval is busy, retrying。 |
CMA –> 连续内存分配器。是一种用于申请大量的,并且物理上连续的内存块的方法。在设备驱动USB,HOST,DMA,ETH PHY中起关键作用。
sstar的内存分配图。如上。LX_MEM有可能有好几块,例如某些SOC上会有双通道DDR,每个DDR上面会各自分配一块LX_MEM。还有某些特别的情况,一颗DDR上面可能会分配多个LX_MEM。多个LX_MEM的命名规则为LX_MEM1、LX_MEM2,以此类推。
注意:MMA Heap以及HW IP Layout分配出来的内存在物理上是连续的,但是LX_MEM分配给linux kernel的不一定是在物理上连续的。
cache是多级的,在一个系统中会有多级cache。
一般来说,在bit-little中,L1在core中,且L1缓存又分为 I-cache指令环境和 D-cache数据缓存。L2 cache在cluster中,L3则在BUS总线上。
当CPU计算时,首先去L1去寻找需要的数据,如果没有则去L2寻找,接着从L3中寻找,如果都没有,则从内存中读取数据。所以,如果某些数据需要经常被访问,那么这些数据存放在L1中的效率会最高。
高速缓存其实就是一组称之为缓存行cache line的固定大小数据块。其大小是以突发写或者突发读的周期大小为基础的。
即使处理器只存取一个字节的存储器,高速缓存控制器也启动整个存取器访问周期并请求整个数据块。缓存行第一个字节的地址总是突发周期尺寸的倍数。缓存行的起始位置总是与突发周期的开头保持一致。
当从内存中取单元到cache中时,会一次取一个cacheline大小的内存区域到cache中,然后存进相应的cacheline中。
很多时候cache都是和MMU一起使用的(即同时开启或关闭)。因为MMU的页表entry属性中控制着内存权限和cache缓存策略等等。
在ARM架构中,L1 cache都是VIPT(virtual index physical tag 虚拟地址做索引,物理地址做tag)的,也就是当有一个虚拟地址送进来,MMU在开始进行地址翻译的时候,Virtual Index就可以去L1 cache中查询了,MMU查询和L1 cache的index查询是同时进行的。如果L1 Miss了,则再去查询L2,L2还找不到则再去查询L3。 注意在arm架构中,仅仅L1是VIPT,L2和L3都是PIPT。