[tgt]Login Phase

  分析分析Login部分。

  iscsi协议的request和response都是成对出现的。也就是一个initiator发出一个reqestment,target就会回一个response。这种方式,从抓包结果,文档,或者tgt的实现里面对连接状态的,和event的处理上都可以看到。只要结合iscsi的这种行为模式,理解起来tgt的实现会容易很多(这里还是要感谢伍哥的点化……)。

  总体上来讲,iscsi在login阶段,request和response的序列是这样子的:

  ---Login initial request

  ---Login initial response(optional)

  ---More Login Requests and Responses(optional)

  ---Login Final-Response(mandatory)

  

  以下是在Login的时候抓到的包。

  

  为什么initiator要发两个Login Command包呢?因为有两个不得不提的字段——CSG和NSG。

  

   iscsi在Login Phase有两个stage ,一个是Security Negotiation,一个是Opertial Parameter Negotiation。CSG 也就是current stage ,NSG也就是next stage。在代码里每个stage会有一个stage net mask,来从flags里面取出CSG和NSG这个字段所代表的stage。既然说了CSG NSG 又不得不提第二行的 T(Transit to next login stage)字段 ,当request的T为1的时候,表示initiator想从current stage(比如上图中current stage就是security negotiation)进入到next stage(比如上图中next stage就是Operational negotiation)。当response中的T为1的时候,自然就表示同意从current stage 进入到 next stage;如果response的T为0,那么就是不同意了。双方意见不一致怎么处理呢,不得不说initiator掌握了说服对方的终极武器——软磨硬泡,initiator会一直发T为1的request。。。。直到response变为1,或者要求initiator收到把T变为0的要求。

   

  这是处理tcp事件handler。可以看到这是一个顺序执行,Login开始的时候target的events是EPOLLIN 然后执行iscsi_rx_handler也就是处理request。在这个过程中会把events设置为EPOLLOUT。返回iscsi_tcp_events_handler时继续顺序执行,执行iscsi_tx_handler,发出一个response。正好印证了抓包结果中request,response的序列。

 1 static void iscsi_tcp_event_handler(int fd, int events, void *data)
 2 {
 3     struct iscsi_connection *conn = (struct iscsi_connection *) data;
 4 
 5     if (events & EPOLLIN)
 6         iscsi_rx_handler(conn);
 7 
 8     if (conn->state == STATE_CLOSE)
 9         dprintf("connection closed\n");
10 
11     if (conn->state != STATE_CLOSE && events & EPOLLOUT)
12         iscsi_tx_handler(conn);
13 
14     if (conn->state == STATE_CLOSE) {
15         dprintf("connection closed %p\n", conn);
16         conn_close(conn);
17     }
18 }
1  conn_write_pdu(conn);
2 conn->tp->ep_event_modify(conn, EPOLLOUT);
3 ret = cmnd_execute(conn);
 1 static int cmnd_execute(struct iscsi_connection *conn)
 2 {
 3     int res = 0;
 4 
 5     switch (conn->req.bhs.opcode & ISCSI_OPCODE_MASK) {
 6     case ISCSI_OP_LOGIN:
 7         cmnd_exec_login(conn);
 8         conn->rsp.bhs.hlength = conn->rsp.ahssize / 4;
 9         hton24(conn->rsp.bhs.dlength, conn->rsp.datasize);
10         break;
11     case ISCSI_OP_TEXT:
12         cmnd_exec_text(conn);
13         conn->rsp.bhs.hlength = conn->rsp.ahssize / 4;
14         hton24(conn->rsp.bhs.dlength, conn->rsp.datasize);
15         break;
16     case ISCSI_OP_LOGOUT:
17         cmnd_exec_logout(conn);
18         conn->rsp.bhs.hlength = conn->rsp.ahssize / 4;
19         hton24(conn->rsp.bhs.dlength, conn->rsp.datasize);
20         break;
21     default:
22         cmnd_reject(conn, ISCSI_REASON_CMD_NOT_SUPPORTED);
23         res = 1;
24         break;
25     }
static void cmnd_exec_login(struct iscsi_connection *conn)
{
    struct iscsi_login *req = (struct iscsi_login *)&conn->req.bhs;
    struct iscsi_login_rsp *rsp = (struct iscsi_login_rsp *)&conn->rsp.bhs;
    int stay = 0, nsg_disagree = 0;

    memset(rsp, 0, BHS_SIZE);
    if ((req->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_LOGIN ||
        !(req->opcode & ISCSI_OP_IMMEDIATE)) {
        cmnd_reject(conn, ISCSI_REASON_PROTOCOL_ERROR);
        return;
    }

    rsp->opcode = ISCSI_OP_LOGIN_RSP;
    rsp->max_version = ISCSI_DRAFT20_VERSION;
    rsp->active_version = ISCSI_DRAFT20_VERSION;
    rsp->itt = req->itt;

    if (/* req->max_version < ISCSI_VERSION || */
        req->min_version > ISCSI_DRAFT20_VERSION) {
        rsp->status_class = ISCSI_STATUS_CLS_INITIATOR_ERR;
        rsp->status_detail = ISCSI_LOGIN_STATUS_NO_VERSION;
        conn->state = STATE_EXIT;
        return;
    }

    switch (ISCSI_LOGIN_CURRENT_STAGE(req->flags)) {
    case ISCSI_SECURITY_NEGOTIATION_STAGE:
        dprintf("Login request (security negotiation): %d\n",
            conn->state);
        rsp->flags = ISCSI_SECURITY_NEGOTIATION_STAGE << 2;

        switch (conn->state) {
        case STATE_FREE:
            conn->state = STATE_SECURITY;
            login_start(conn);
            if (rsp->status_class)
                return;
            /* fall through */
        case STATE_SECURITY:
            text_scan_security(conn);
            if (rsp->status_class)
                return;
            if (conn->auth_method != AUTH_NONE) {
                conn->state = STATE_SECURITY_AUTH;
                conn->auth_state = AUTH_STATE_START;
            }
            break;
        case STATE_SECURITY_AUTH:
            switch (cmnd_exec_auth(conn)) {
            case 0:
                break;
            default:
            case -1:
                goto init_err;
            case -2:
                goto auth_err;
            }
            break;
        default:
            goto init_err;
        }

        break;
    case ISCSI_OP_PARMS_NEGOTIATION_STAGE:
        dprintf("Login request (operational negotiation): %d\n",
            conn->state);
        rsp->flags = ISCSI_OP_PARMS_NEGOTIATION_STAGE << 2;

        switch (conn->state) {
        case STATE_FREE:
            conn->state = STATE_LOGIN;

            login_start(conn);
            if (account_available(conn->tid, AUTH_DIR_INCOMING))
                goto auth_err;
            if (rsp->status_class)
                return;
            text_scan_login(conn);
            if (rsp->status_class)
                return;
            stay = text_check_param(conn);
            break;
        case STATE_LOGIN:
            text_scan_login(conn);
            if (rsp->status_class)
                return;
            stay = text_check_param(conn);
            break;
        default:
            goto init_err;
        }
        break;
    default:
        goto init_err;
    }

    if (rsp->status_class)
        return;
    if (conn->state != STATE_SECURITY_AUTH &&
        req->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
        int nsg = ISCSI_LOGIN_NEXT_STAGE(req->flags);

        switch (nsg) {
        case ISCSI_OP_PARMS_NEGOTIATION_STAGE:
            switch (conn->state) {
            case STATE_SECURITY:
            case STATE_SECURITY_DONE:
                conn->state = STATE_SECURITY_LOGIN;
                login_security_done(conn);
                break;
            default:
                goto init_err;
            }
            break;
        case ISCSI_FULL_FEATURE_PHASE:
            switch (conn->state) {
            case STATE_SECURITY:
            case STATE_SECURITY_DONE:
                if ((nsg_disagree = text_check_param(conn))) {
                    conn->state = STATE_LOGIN;
                    nsg = ISCSI_OP_PARMS_NEGOTIATION_STAGE;
                    break;
                }
                conn->state = STATE_SECURITY_FULL;
                login_security_done(conn);
                break;
            case STATE_LOGIN:
                if (stay)
                    nsg = ISCSI_OP_PARMS_NEGOTIATION_STAGE;
                else
                    conn->state = STATE_LOGIN_FULL;
                break;
            default:
                goto init_err;
            }
            if (!stay && !nsg_disagree) {
                login_finish(conn);
                if (rsp->status_class)
                    return;
            }
            break;
        default:
            goto init_err;
        }
        rsp->flags |= nsg | (stay ? 0 : ISCSI_FLAG_LOGIN_TRANSIT);
    }

先放点代码 ,太晚了 剩下分析的有空补。主要包括stage的转换 和connection的状态的转换。

  

 

posted on 2016-08-04 01:03  剑客西门吹雪  阅读(759)  评论(0编辑  收藏  举报

导航