linux I/O栈 之 SCSI TARGET (2) iscsi target-<1>协议

      1、iSCSI  概念

    

    我把iscsi target作为TARGET的第一篇来讲,主要因为很多人知道I/O导出协议,也都是第一个想到iscsi。

    iscsi是什么。首先,我们来讲讲它的概念。

    

SCSI,小型计算机系统接口,是基于client-server模型设计的存储接口,SCSI的client(initiator)将命令发送给server(target),server处理命令后回应result给client。由于要传送命令必然要用到相应的传输协议保障,iSCSI就是其中的一种传输协议,除此之外还有FC和SAS等。

iSCSI是SCSI over IP缩写,它使用TCP/IP协议来传递SCSI命令与响应,其借用现有成熟廉价的以太网来实现SCSI通讯,成本低,管理和使用非常方便。

iSCSI有initiator和target两个部分,分别对应于SCSI的initiator和target,iSCSI的initiator与target使用TCP进行传输协议实现了SCSI的initiator与target之间的数据传输。

2、iSCSI 协议基本概念

SCSI target中包含许多LU(逻辑单元),每一个SCSI命令都有其目标LU,这些LU负责对针对其的SCSI命令进行处理与响应。iSCSI的命令单元是PDU,在iSCSI initiator端,PDU包含SCSI initiator下发给其的LUN(逻辑单元号码)及SCSI命令(CDB),PDU被作为TCP的playload传递到iSCSI target端后由SCSI target core根据LUN将SCSI命令投放到目标LU中,LU在处理命令后把响应结果递交给SCSI target,SCSI target再将其递交给iSCSI target,iSCSI target把result封装成iSCSI PDU,再使用TCP回传给iSCSI initiator,iSCSI initiator再将结果递交给上层。

linux I/O栈 之 SCSI TARGET (2)  iscsi target-<1>协议

        3、iSCSI 子系统成员概念

        

iSCSI Device

利用iSCSI协议传递服务子系统的SCSI设备,典型的就是IP-SAN了。

iSCSI Node

iSCSI节点表示一个iSCSI启动器或者iSCSI目标器。在一个网络实体里,可能有一或者多个iSCSI节点。可以通过一个或者多个网络端口访问iSCSI节点。iSCSI节点的标识是iSCSI名称。把iSCSI名称和地址分开来后,允许多个iSCSI节点使用同一个地址,或者一个iSCSI节点使用多个地址,它是组成IP-SAN fabric的基本单元。

Network Entity

     网络实体。它表示一个可以通过IP网络访问的设备或者网关。一个网络实体必须有一或者多个网络端口,包含在该网络实体内的iSCSI节点可以用这些端口来访问IP网络。

Network Portal

网络端口。网络实体的一个组成部分,它有一个TCP/IP地址。网络端口在initiator用IP地址标识,在target用IP地址+侦听的TCP端口标识。

Session

连接initiator和target的一组TCP连接构成一个session(可以简单理解为I_T nexus)。可以向session添加TCP连接,也可以把TCP连接从session删除。通过一个session的所有连接,initiator只看到同一个target。

Connection

    一对TCP连接。Initiator和target之间可以使用一或者多个TCP连接通信。

CID(Connection ID)

一个session里的每个connection用CID进行标识,该标识在session范围内是唯一。CID由initiator产生,在login请求和使用logout关闭连接时传递给target。

Portal Groups

网络端口组。iSCSI session支持多连接,一些实现能把通过多个端口建立的多个连接捆绑到一个session。一个iSCSI网络实体的多个网络端口被定义为一个网络端口组,把该组和一个session联系起来,该session就可以捆绑通过该组内多个端口建立的多个连接,再使它们一起协同工作以达到捆绑的目的。每一个该组的session并不需要包括该组的所有网络端口。一个iSCSI节点可能有一或者多个网络端口组,但是每一个iSCSI使用的网络端口只能属于iSCSI节点的一个组。

Portal Group Tag

网络端口组标识。使用16比特的数标识一个网络端口组。在一个iSCSI节点里,所有具有同样组标志的端口构成一个网络端口组。

 I_T nexus

I_T nexus是指一个SCSI initiator的端口和一个SCSI target端口之间的关系。对于iSCSI,这个关系对应一个session,它指session的initiator端和iSCSI target网络端口组之间的关系。I_T nexus的标识是一对端口名称(iSCSI initiator名称+i+ISID,iSCSI target名称+t+网络端口组标识)。

PDU (Protocol Data Unit): initiator和target之间通信时把信息分割为消息。这些消息称为iSCSI PDU。

SSID (Session ID)

iSCSI initiator和iSCSI target之间的session用SSID进行标识,该标识由initiator部分的ISID和target部分的TPGT构成。ISID由initiator在session建立的时候明确给出,TPGT隐含的由建立连接时选择的网络端口确定。当TargetName给定后,建立连接的过程中,target必须把TPGT关键字当作确认信息发送出去。

TSIH (Target Session Identifying Handle): Target分配给与特定名称initiator建立的session的标识。但是0被保留着用于initiator告知target这是一个新session。在为一个session添加一个connect时,TSIH已经隐含指明。

        4、iscsi网络映射关系


在SCSI arch中,一个Device(SCSI initiator或者SCSI target)可以包含多个ports,一个SCSI initiator port和一个SCSI target port之间的联结就是一个I_T nexus,一个SCSI initiator port和一个SCSI target port之间只允许建立一个I_T nexus。

I_T nexus对应与iSCSI中的一个session(normal),SCSI target port对应于iSCSI target中的一个portal group(具有相同tag的一组ports,iSCSI target在这些ports上进行监听等待iSCSI initiator的连接登陆login,所有这些ports一起支撑一个I_T nexus),SCSI initiator port对应于iSCSI initiator中的session(normal)的端口,如下图所示。

linux I/O栈 之 SCSI TARGET (2)  iscsi target-<1>协议

一个iSCSI节点只能有且只有一个SCSI设备;只能通过正常运行的iSCSI session访问SCSI设备;SCSI名称也就是iSCSI节点的名称。

SCSI initiator端口,它被映射到一定正常运行的iSCSI session的一端。在login过程中,一个iSCSI initiator节点和一个iSCSI target节点协商建立一个正常运行的session;当session成功建立后,一个隶属于iSCSI initiator设备的SCSI initiator端口被创建(虚拟出一个port)。SCSI initiator端口的名称和标识被定义为: iSCSI initiator name + i+ ISID标识。

对于iSCSI,SCSI initiator端口和SCSI target端口的定义是不一样的。iSCSI target端口,映射为iSCSI target的一个网络端口组中。

iSCSI target端口名称和标识:都被定义为:iSCSI target name + t + iSCSI target PGT(port group tag)。

       5、iSCSI协议中的命名及标识和登录过程

每个iSCSI initiator或者iSCSI target都有一个全球唯一的名称,对于iSCSI initiator称为initiator name,对于iSCSI target称为target name。

iSCSI target中的port group使用port group tag来标识;

session在iSCSI initiator端使用ISID(initiator session identifier)标识,在iSCSI target端使用TSIH(target session identifier handle)标识。

四、session和connection的状态划分及登陆登出。对于connection,有两种状态,即login phase(登陆状态)和full function phase(全功能状态),登陆状态即iSCSI initiator与iSCSI target正处于登陆协商过程;全功能状态即登陆过程已经完成,该connection已经能为上层SCSI提供服务。Session与connection一样也有login phase和full function phase,当session中仅有的一个connection处于登陆状态时,session处于登陆状态,当session至少有一个connection处于全功能状态时session处于全功能状态。

iSCSI initiator登陆到iSCSI target的过程分三个过程:

1)  首先是iSCSI initiator往iSCSI target发送initial login请求,请求中包括iSCSI initiator支持的协议版本、iSCSI initiator name和iSCSI target name、ISID和TSIH及CID;

2)  其次是安全协商,也就是chap认证之类,用于iSCSI initiator和iSCSI target互相之间的授权认证;

3)  最后是操作参数协商,用于协商在全功能时期的一些通讯参数。

有两种登出方式,一种是iSCSI initiator主动发出logout请求,在收到iSCSI target的logout响应后完成登出;另一种就是iSCSI target发送包含“request logout”的Async协议数据包,启动器再发送logout请求。

       linux I/O栈 之 SCSI TARGET (2)  iscsi target-<1>协议

       5  iSCSI PDU格式

        

1. 一个Basic Header Segment(BHS,48字节);

2. 可选地n(任意)个Additional Header Segment(AHS,长度不固定,字节数为4的整数倍(不够必须填补0),iSCSI target发往iSCSI initiator的PDU中不包含AHS);

3. 可选地一个Header Digest(头校验信息,4个字节);

4. 可选地一个Data Segment(数据段,长度不固定,字节数为4的整数倍(不够必须填补0));

5. 可选的一个Data Digest(数据校验信息,4个字节)。

        linux I/O栈 之 SCSI TARGET (2)  iscsi target-<1>协议

        5.1 BHS(Basic Header Segment格式

        linux I/O栈 之 SCSI TARGET (2)  iscsi target-<1>协议

            

1. I bit:1bit,标识本PDU为需要马上发送;

2. Opcode操作码:6bit,标识本PDU的类型;

   (1)由iSCSI initiator发往iSCSI target的请求PDU opcode 

        

        0x00 NOP-Out
        0x01 SCSI Command (encapsulates a SCSI Command Descriptor Block)
        0x02 SCSI Task Management function request
        0x03 Login Request
        0x04 Text Request
        0x05 SCSI Data-Out (for WRITE operations)
        0x06 Logout Request
        0x10 SNACK Request
        0x1c-0x1e Vendor specific codes

(2)由iSCSI target发往iSCSI initiator的相应PDU opcode 

        0x20 NOP-In
        0x21 SCSI Response - contains SCSI status and possibly sense information or other response information.
        0x22 SCSI Task Management function response
        0x23 Login Response
        0x24 Text Response
        0x25 SCSI Data-In - for READ operations.
        0x26 Logout Response
        0x31 Ready To Transfer (R2T) - sent by target when it is ready to receive data.
        0x32 Asynchronous Message - sent by target to indicate certain special conditions.
        0x3c-0x3e Vendor specific codes
        0x3f Reject

3. F bit:1bit,标识本PDU为一个PDU序列中的最后一个;

4. Total AHS Length:1byte,本PDU中所有AHS的长度,以4字节为单位,包括可能需要填补的0(padding);

5. Data Segment Length:3byte,本PDU中数据段的长度,以字节为单位,不包括可能需要填补的0(padding);

6. LUN:如果此(命令/响应/数据)PDU与具体的LU相关联,则使用LUN(Logical Unit Number)标识该LU;

7. Initial Task Tag(ITT):在一个session中使用ITT标识iSCSI initiator发起的任何iSCSI任务,凡是与某iSCSI 任务相关联的PDU,不管是由iSCSI initiator发出的还是由iSCSI target发出的,都携带该iSCSI 任务相应的ITT,这个ITT就是scsi cmd的命令标识符,它代表一个scsi cmd。

    

    6 pdu的传输

  6.1 SCSI read命令

    ① iSINI(iscsi initiator)发送 SCSI cmd pdu(OP=R,F=1)给iSTGT,①pdu seq结束

    ② iSTGT向ISINI发送data pdu和respone pdu。

      data pdu包含三次datain pdu,dataSN(seq number)在递增,并且在最后一个标记F=1,pdu seq结束。

      response pdu会带着ExpDataSN,它代表此次iSCSI taget已发送的DATA PDU的个数。

   linux I/O栈 之 SCSI TARGET (2)  iscsi target-<1>协议

        

            6.2   SCSI写命令

              iscsi 写命令的流程,包括立即数据和非立即数据的两种情况。

              ① iSINI 发送scsi cmd pdu(op=w,F=0,I=1(immediate)),包含立即数据。

                 iSINI dataout pdu(DataSN=0,F=1) 值得是当前的dataout seq结束。

              ② 接下来的流程就是r2t(ready to transfer)和dataout的交互。

linux I/O栈 之 SCSI TARGET (2)  iscsi target-<1>协议



    7 iSCSI错误处理


错误修复范围(class)within command

目标器:Write命令时目标器丢失dataout pdu(digest error or sequence error)。解决:使用recovery R2T。

启动器:Read命令时启动器丢失datain pdu或者r2t pdu(digest error or sequence error)。解决:使用snack。

within connection

启动器:发出的命令没有得到响应(ExpCmdSN)、丢失response pdu(digest error or sequence error)。解决:前者使用retry,后者使用snack。

目标器:发出response pdu没有得到启动器的确认(ExpStatSN)。解决:使用nop-in通知启动器使启动器发送snack要求重传输。

connection recovery

启动器:检测到tcp connection失败或者接收到目标器异步消息(Asynchronous Message)通知其tcp connection失败。解决:connection recovery(task reassign)。

目标器:检测到tcp connetion失败。解决:关闭connection,如果还有connection可用则通过异步消息通知启动器tcp connection失败

session recovery

其它三种修复尝试后,才考虑使用session修复。

Session修复对于启动器:关闭所有的连接connection,中止所有未决命令以恰当的结果返回上层SCSI,重新建立一个新的session连接到目标器。

错误修复层次/级别(hierarchy/level)

错误修复层次/级别用于定义iSCSI initiator/iSCSI target修复错误的能力,分级如下:

0级,不管遇到什么错误仅能进行session recovery,修复能力很差。

1级,能在command、connection范围内进行修复,同时拥有session recovery能力,遇到错误先考虑within command修复,不行再考虑within connection修复,最后才会考虑session recovery。

2级,能在command、connection范围内进行修复,同时拥有connection recovery、session recovery能力,遇到错误先考虑within command修复,不行再考虑within connection修复,不行再考虑connection recovery,最后才会考虑session recovery。

task reassign

在iSCSI中,每个iSCSI命令(task)都与唯一的一个connection关联,有关该命令的所有pdu都需要在该connection上发送/接收,在进行connection recovery之后,iSCSI initiator可以考虑进行task reassign,就是把原connection上的task(iSCSI命令)重新关联到另一个或者新的connection,这要通过在新connection上发送iSCSI任务管理命令――task reassign来完成。

retry

当iSCSI initiator往iSCSI target发送了iSCSI cmd pdu但却收不到iSCSI target的确认(通过ExpCmdSN或者datain/r2t pdu或者response pdu),此时iSCSI initiator需要重新发送该iSCSI cmd pdu(需要使用跟原来一样的ITT及CmdSN)。

reject pdu

如果reject是针对cmd pdu,则意味着iSCSI target要丢弃该cmd;如果是针对非cmd pdu,则iSCSI target必须还要发送一个response pdu来完成该命令,同时iSCSI initiator可以选择是否重传输被reject了的pdu。

Digest Error

对于header digest error,会接收并丢弃pdu中的数据或者直接关闭当前connection。

payload(data) digest error

iSCSI initiator直接丢弃该pdu,如果该pdu是datain pdu,则发送一个data SNACK请求重传输或者可以中止该任务(任务管理命令)从而完成该命令。如果该pdu是response pdu,则发送一个status SNACK请求重传输,或者进行connection修复(在新connection完成该命令),或者直接关闭该connection。如果该pdu是一个非请求pdu(如Async,Reject等)则无需进一步的处理。iSCSI target响应一个reject pdu,pdu中reason设为Data-Digest-Error,然后丢弃该pdu。如果该pdu是一个dataout pdu(r2t请求的或者非请求的),则发送r2t请求重传输,或者响应一个cmd response pdu 中止该任务(完成该命令)(Status字段为“CHECK CONDITION”,sense data为“protocol service CRC error”)。如果该pdu不是一个dataout pdu,则无需进一步的处理。

Sequense Error

当iSCSI initiator接收到一个R2TSN/DataSN乱序的r2t/datain pdu或者一个内含ExpDataSN指示有r2t/datain pdu丢失的response pdu,意味着iSCSI initiator之前曾接收到过一个或多个Digest Error的r2t/datain pdu。

当iSCSI target接收到一个DataSN乱序的dataout pdu,则意味着iSCSI target之前至少接收到过一个Digest Error的dataout pdu,使用1.7.3介绍的方法进行处理。

当iSCSI initiator接收到一个StatSN乱序的response pdu时,意味着之前曾接收到过digest error的response pdu,所以使用1.7.3介绍的方法进行处理就行了。

format error

如果iSCSI initiator或者iSCSI target检测到接收到的pdu格式错误(内容非法或者内容不一致),则会关闭当前session并重建一个session(session recovery)

  8  pdu分类和概述

        

    8.1只携带SCSI信息的PDU

   8.1.1 SCSI Command

iSCSI initiatoràiSCSI target。PDU中包含SCSI CDB(Command Description Block)和命令执行需要的所有参数(如任务属性,希望传输的数据的长度,LUN,ITT等);也包含iSCSI协议通讯需要用到的信息:

CmdSN(session范围内iSCSI命令的序号,与ExpCmdSN一起用于检测iSCSI命令是否在传输过程中有丢失)。

ExpStatSN(期望iSCSI target发送的connection状态序号,用于向iSCSI target确认该connection中所有StatSN小于ExpStatSN的状态响应都已被iSCSI initiator接收到,与StatSN一起用于检测含Status信息的iSCSI pdu是否在传输过程中有丢失);

8.1.2 SCSI Response

iSCSI targetàiSCSI initiator,用于iSCSI target反馈SCSI Command的执行结果。PDU中包含SCSI命令执行的结果,是没有被执行还是已经被执行但执行中出现一些问题;如果命令中进行了数据传输,还包括数据传输的偏差值Residual count(如iSCSI initiator起初表明要写5k字节,目标器接收到了5k+2字节等,Residual count就为2)。SCSI命令执行结果字段:response和status,如果response为0(command completed)而status为2(check condition),将会有sense data(含有check condition的详细信息)包含在pdu的data字段中。

iSCSI协议通讯需要用到的字段:

ExpCmdSN

session范围内iSCSI target期望iSCSI initiator发送的命令序号,用于向iSCSI initiator确认所有CmdSN小于ExpCmdSN的命令都已被iSCSI target接收到。

MaxCmdSN

session中iSCSI initiator能够发送的最大命令序号,用于构建一个窗口缓冲区,匹配iSCSI initiator和iSCSI target的命令发送速率和命令处理速率。

StatSN

connection范围内iSCSI响应pdu的状态序号,用于状态响应编号),

ExpDataSN(本命令中iSCSI target已发送Data PDU的个数。

Task Management Function Request

iSCSI initiatoràiSCSI target,用于iSCSI initiator控制管理(如中止)之前发往iSCSI target的多个任务。

Task Management Function Response

iSCSI targetàiSCSI initiator,用于iSCSI target反馈任务管理请求的执行结果

SCSI Data Out和SCSI Data In

iSCSI initiatorßàiSCSI target,用于传输SCSI数据。

PDU中除了包含SCSI数据外,还包含用于iSCSI协议通讯的信息:所携带数据在命令所要传送的所有数据中的Buffer Offset和Data Segment Length。

DataSN

数据在一个Data PDU序列中的序号,用于检测是否有Data PDU在传输中丢失。

Ready To Transfer(R2T)

iSCSI initiatorßiSCSI target,用于iSCSI initiator往iSCSI target传输数据时,iSCSI target通知iSCSI initiator其已准备好接收数据了,收到此PDU后iSCSI initiator就可以往iSCSI target发送数据了。

Target Transfer Tag(TTT)

iSCSI initiator针对此PDU发送的Data Out PDU中包含一样的TTT以标识此数据包与此R2T的关联性。

R2TSN

是在一个iSCSI命令范围内R2T的序号,iSCSI initiator据此可以检测到R2T pdu的丢失。


8.2携带SCSI信息和iSCSI信息的PDU

Asynchronous Message

异步消息是一种iSCSI target可以主动向iSCSI initiator发送的pdu,用于告知iSCSI initiator一些事件。异步消息pdu中可以是SCSI消息,也可以是iSCSI消息。

Pdu中有一个字段AsyncEvent用于指示具体的事件,如为0表示是SCSI异步事件,具体内容放置在pdu的data部分(称为sense data);如为1表示iSCSI target要求iSCSI initiator登出(logout);等等。


8.3只携带iSCSI信息的PDU

Text request和Text response

文本请求和文本响应用于实现参数(启动器和目标器在通讯时要使用到,具体参见RFC3720第12章)的协商。Pdu中的data部分以形式“key=value”出现。

多个文本请求/响应pdu内包含相同的ITT来标识这些请求/响应属于同一个协商操作。此外,Pdu中的TTT使用不同的机制来标识是否继续一个协商操作还是启动一个新的协商操作。

login request和login response

登陆请求和响应用来实现iSCSI initiator登陆到iSCSI target。Pdu header内含:

(1) 登陆阶段信息T bit、C bit、CSG、NSG(登陆分两个阶段,安全协商阶段和操作参数协商阶段);

(2) ISID和TSIH(分别在iSCSI initiator和iSCSI target中唯一地标识session),指示本次登陆session的ID;

CID(在session中唯一的标识connection),指示本次登陆connection的ID;

(3) CmdSN、ExpStatSN(login request);

(4) StatSN、ExpCmdSN、MaxCmdSN(login response)。

Pdu 的data部分与文本请求/响应pdu一样以形式“key=value”出现,内含登陆参数,包括安全协商参数(参见RFC3720第11章)和操作参数(参见RFC3720第12章)。

一个session的第一个connection的登陆过程会确定/协商整个session范围内的一些参数,如iSCSI协议版本号、session ID、session内允许创建的connection个数。

logout request和logout response

(1)登出请求和响应用来实现iSCSI登出iSCSI target,登出request pdu中包含登出的原因:

a.关闭整个session;

b.关闭session中的某个connecton(可以是发送登出请求的connection本身由pdu header中的CID确定);

c.删除session中的某个connecton以便进行修复,原connection中的未决iSCSI命令都将被转移到另外的connection中进行传输处理,也就是所谓的connection recovery。

(2)登出response pdu header中包含response字段反馈登出请求的响应结果:

a.成功关闭session或者connection;

b.找不到connection;

c.iSCSI target不支持connection recovery;

d.关闭session/connection失败或者修复connection失败。

如果是connection修复响应pdu,pdu header中还包含两个字段:

Time2Wait:如果iSCSI initiator要进行把任务(iSCSI命令)重新指派到别的connection(task reasign),至少要等待的时间,单位为秒;

Time2Retain:iSCSI target在Time2Wait之后会等待iSCSI initiator进行task reasign的时间,超过这个时间后将丢弃该任务(iSCSI命令)。

SNACK request

SNACK就是对SN的ack,就是对各种有序号的pdu包括包含Status/response信息的pdu、datain pdu、r2t pdu进行确认(对于无误的情况)或者要求iSCSI target重传输(对于出错的情况)。

具体而言在SNACK request pdu header中包含字段Type:

a.请求datain pdu或者r2t pdu的重传输

1.请求包含Status/Response信息的pdu的重传输

2.确认接收到datain pdu(针对A bit被置位的datain pdu)

3.在MaxRecvDataSegmentLength被改变后请求datain pdu的重传输

另外两个字段BegRun和RunLength指示需要重传输的pdus的起始SN及pdu个数(重传输时)或者指示期待的下一个datain pdu的SN(确认接收时)。

reject

reject pdu用于向iSCSI initiator提示一个iSCSI错误,表示拒绝之前的iSCSI initiator发送过来的某一个pdu。Pdu header中含一个指示拒绝原因的字段Reason:

a.pdu的数据字段校验错误,需要iSCSI initiator的重传输

b.拒绝之前的一个SNACK pdu,需要重传输

c.协议错误

d.不支持该iSCSI命令

此外reject pdu还把被拒绝的pdu的header部分拷贝到reject pdu的data部分发送给iSCSI initiator。

nop in和nop out

nop in和nop out用于iSCSI initiator和iSCSI target周期性的检测其对端是否还处于可操作状态,相当于TCP/IP协议中的“ping”。

由iSCSI initiator主动发起并要求iSCSI target响应的nop out pdu必须包含一个有效的ITT(非全1),此外的其它nop out pdu所包含的ITT必须为全1。

由iSCSI target主动发起并要求iSCSI initiator响应的nop in pdu必须包含一个有效的TTT(非全1),此外的其它nop in pdu所包含的TTT必须为全1。

由iSCSI initiator响应iSCSI target的nop in pdu(内含非全1的TTT)而发送的nop out pdu必须包含与其响应的nop in pdu一样的TTT,此外其它nop out pdu所包含的TTT都为全1。

由iSCSI target响应iSCSI initiator的nop out pdu(内含非全1的ITT)而发送的nop in pdu必须包含与其响应的nop out pdu一样的ITT,此外其它nop in pdu所包含的ITT必须为全1。

    


【本文只在51cto博客作者 “底层存储技术” https://blog.51cto.com/12580077  个人发布,公众号发布:存储之谷】,如需转载,请于本人联系,谢谢。

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/192505.html

(0)
上一篇 2021年11月15日
下一篇 2021年11月15日

相关推荐

发表回复

登录后才能评论