Open vSwitchのソースコードを読む(8) OpenFlow channel の管理

2013-06-18 by Daisuke Kotani

Connection Manager, OpenFlow listener and OpenFlow connection

ofproto 内で、OpenFlow Controller との接続に関する処理を行っているのが Connection Manager です。ofproto/connmgr.c にあります。

これに関する構造体は3つあります。それぞれのConnectionに関する情報は、ofconn構造体を用いて管理されています。また、OpenFlowスイッチがControllerからの接続を受け付けるとき(listenするとき)のデフォルトの値などは ofservice 構造体に記録されています。connmgr 構造体はスイッチ全体でConnectionを管理するためのもので、ofconn 構造体や ofservice 構造体へのポインタなどを持っています。

/* Connection manager for an OpenFlow switch. */
struct connmgr {
    struct ofproto *ofproto;
    char *name;
    char *local_port_name;

    /* OpenFlow connections. */
    struct hmap controllers;   /* Controller "struct ofconn"s. */
    struct list all_conns;     /* Contains "struct ofconn"s. */
    uint64_t master_election_id; /* monotonically increasing sequence number
                                  * for master election */
    bool master_election_id_defined;

    /* OpenFlow listeners. */
    struct hmap services;       /* Contains "struct ofservice"s. */
    struct pvconn **snoops;
    size_t n_snoops;

    /* Fail open. */
    struct fail_open *fail_open;
    enum ofproto_fail_mode fail_mode;

    /* In-band control. */
    struct in_band *in_band;
    struct sockaddr_in *extra_in_band_remotes;
    size_t n_extra_remotes;
    int in_band_queue;
};

struct hman controllers, struct list all_conns あたりがそれぞれのcontrollerとのconnectionを管理するofconn構造体へのポインタ(を入れるためのリストだったりハッシュテーブルだったり)です。

/* An OpenFlow connection. */
struct ofconn {
/* Configuration that persists from one connection to the next. */

    struct list node;           /* In struct connmgr's "all_conns" list. */
    struct hmap_node hmap_node; /* In struct connmgr's "controllers" map. */

    struct connmgr *connmgr;    /* Connection's manager. */
    struct rconn *rconn;        /* OpenFlow connection. */
    enum ofconn_type type;      /* Type. */
    enum ofproto_band band;     /* In-band or out-of-band? */
    bool enable_async_msgs;     /* Initially enable async messages? */

/* State that should be cleared from one connection to the next. */

    /* OpenFlow state. */
    enum nx_role role;           /* Role. */
    enum ofputil_protocol protocol; /* Current protocol variant. */
    enum nx_packet_in_format packet_in_format; /* OFPT_PACKET_IN format. */

    /* Asynchronous flow table operation support. */
    struct list opgroups;       /* Contains pending "ofopgroups", if any. */
    struct ofpbuf *blocked;     /* Postponed OpenFlow message, if any. */
    bool retry;                 /* True if 'blocked' is ready to try again. */

    /* OFPT_PACKET_IN related data. */
    struct rconn_packet_counter *packet_in_counter; /* # queued on 'rconn'. */
#define N_SCHEDULERS 2
    struct pinsched *schedulers[N_SCHEDULERS];
    struct pktbuf *pktbuf;         /* OpenFlow packet buffers. */
    int miss_send_len;             /* Bytes to send of buffered packets. */
    uint16_t controller_id;     /* Connection controller ID. */

    /* Number of OpenFlow messages queued on 'rconn' as replies to OpenFlow
     * requests, and the maximum number before we stop reading OpenFlow
     * requests.  */
#define OFCONN_REPLY_MAX 100
    struct rconn_packet_counter *reply_counter;

    /* Asynchronous message configuration in each possible roles.
     *
     * A 1-bit enables sending an asynchronous message for one possible reason
     * that the message might be generated, a 0-bit disables it. */
    uint32_t master_async_config[OAM_N_TYPES]; /* master, other */
    uint32_t slave_async_config[OAM_N_TYPES];  /* slave */

    /* Flow monitors. */
    struct hmap monitors;       /* Contains "struct ofmonitor"s. */
    struct list updates;        /* List of "struct ofpbuf"s. */
    bool sent_abbrev_update;    /* Does 'updates' contain NXFME_ABBREV? */
    struct rconn_packet_counter *monitor_counter;
    uint64_t monitor_paused;
};

たくさんメンバー変数がありますが、最初は親のconnmgrへのポインタなど、次にOpenFlowの状態、Packet-In の制限に関するもの、などが含まれています。

/* A listener for incoming OpenFlow "service" connections. */
struct ofservice {
    struct hmap_node node;      /* In struct connmgr's "services" hmap. */
    struct pvconn *pvconn;      /* OpenFlow connection listener. */

    /* These are not used by ofservice directly.  They are settings for
     * accepted "struct ofconn"s from the pvconn. */
    int probe_interval;         /* Max idle time before probing, in seconds. */
    int rate_limit;             /* Max packet-in rate in packets per second. */
    int burst_limit;            /* Limit on accumulating packet credits. */
    bool enable_async_msgs;     /* Initially enable async messages? */
    uint8_t dscp;               /* DSCP Value for controller connection */
    uint32_t allowed_versions;  /* OpenFlow protocol versions that may
                                 * be negotiated for a session. */
};

これは、接続を受け付けたときのofconnのデフォルト値を設定しておくもので、Packet-In のrate や probe の間隔、受け付けるOpenFlowのバージョンなどが入っています。

connmgr_run

main loop から、Controllerとの接続のに関する処理を行うために呼び出されるのがconnmgr_run関数です。ofproto/connmgr.cにあります。 この中では、それぞれのconnectionの処理を行う関数(ofconn_run)や、新規のコネクションを受け付ける処理が行われています。

/* Does all of the periodic maintenance required by 'mgr'.
 *
 * If 'handle_openflow' is nonnull, calls 'handle_openflow' for each message
 * received on an OpenFlow connection, passing along the OpenFlow connection
 * itself and the message that was sent.  If 'handle_openflow' returns true,
 * the message is considered to be fully processed.  If 'handle_openflow'
 * returns false, the message is considered not to have been processed at all;
 * it will be stored and re-presented to 'handle_openflow' following the next
 * call to connmgr_retry().  'handle_openflow' must not modify or free the
 * message.
 *
 * If 'handle_openflow' is NULL, no OpenFlow messages will be processed and
 * other activities that could affect the flow table (in-band processing,
 * fail-open processing) are suppressed too. */
void
connmgr_run(struct connmgr *mgr,
            bool (*handle_openflow)(struct ofconn *, struct ofpbuf *ofp_msg))
{
    struct ofconn *ofconn, *next_ofconn;
    struct ofservice *ofservice;
    size_t i;

    if (handle_openflow && mgr->in_band) {
        if (!in_band_run(mgr->in_band)) {
            in_band_destroy(mgr->in_band);
            mgr->in_band = NULL;
        }
    }

    LIST_FOR_EACH_SAFE (ofconn, next_ofconn, node, &mgr->all_conns) {
        ofconn_run(ofconn, handle_openflow);
    }
    ofmonitor_run(mgr);

    /* Fail-open maintenance.  Do this after processing the ofconns since
     * fail-open checks the status of the controller rconn. */
    if (handle_openflow && mgr->fail_open) {
        fail_open_run(mgr->fail_open);
    }

    HMAP_FOR_EACH (ofservice, node, &mgr->services) {
        struct vconn *vconn;
        int retval;

        retval = pvconn_accept(ofservice->pvconn, &vconn);
        if (!retval) {
            struct rconn *rconn;
            char *name;

            /* Passing default value for creation of the rconn */
            rconn = rconn_create(ofservice->probe_interval, 0, ofservice->dscp,
                                 vconn_get_allowed_versions(vconn));
            name = ofconn_make_name(mgr, vconn_get_name(vconn));
            rconn_connect_unreliably(rconn, vconn, name);
            free(name);

            ofconn = ofconn_create(mgr, rconn, OFCONN_SERVICE,
                                   ofservice->enable_async_msgs);
            ofconn_set_rate_limit(ofconn, ofservice->rate_limit,
                                  ofservice->burst_limit);
        } else if (retval != EAGAIN) {
            VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
        }
    }

    for (i = 0; i < mgr->n_snoops; i++) {
        struct vconn *vconn;
        int retval;

        retval = pvconn_accept(mgr->snoops[i], &vconn);
        if (!retval) {
            add_snooper(mgr, vconn);
        } else if (retval != EAGAIN) {
            VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
        }
    }
}

気にしておきたいのは、OpenFlowのメッセージを処理する関数のポインタがhandle_openflowという名前で引数で指定されているということです。OpenFlowのメッセージがどのように処理されるのかはhandle_openflowで指定された関数の中を見る必要があります。

connmgr_run は ofproto/ofproto.c の ofproto_run から以下のように呼び出されています。

connmgr_run(p->connmgr, handle_openflow);

ということで、OpenFlow メッセージの処理を追うためには、ofproto/ofproto.c の handle_openflow 関数を読んでいく必要があります。

ofconn_run: それぞれのchannelの処理

connmgr_run から、それぞれの OpenFlow channel の処理を行うために、ofconn_run 関数が実行されています。これは ofproto/connmgr.c にあります。

static void
ofconn_run(struct ofconn *ofconn,
           bool (*handle_openflow)(struct ofconn *, struct ofpbuf *ofp_msg))
{
    struct connmgr *mgr = ofconn->connmgr;
    size_t i;

    for (i = 0; i < N_SCHEDULERS; i++) {
        pinsched_run(ofconn->schedulers[i], do_send_packet_in, ofconn);
    }

ここまでは、Packet-In を送っているところ。 :::c rconn_run(ofconn->rconn);

これは、コネクションを維持するための処理を実行する関数 rconn_run を実行しているところ。lib/rconn.c にあります。

    if (handle_openflow) {
        /* Limit the number of iterations to avoid starving other tasks. */
        for (i = 0; i < 50 && ofconn_may_recv(ofconn); i++) {
            struct ofpbuf *of_msg;

            of_msg = (ofconn->blocked
                      ? ofconn->blocked
                      : rconn_recv(ofconn->rconn));
            if (!of_msg) {
                break;
            }
            if (mgr->fail_open) {
                fail_open_maybe_recover(mgr->fail_open);
            }

            if (handle_openflow(ofconn, of_msg)) {
                ofpbuf_delete(of_msg);
                ofconn->blocked = NULL;
            } else {
                ofconn->blocked = of_msg;
                ofconn->retry = false;
            }
        }
    }

もし、OpenFlowのメッセージを処理する関数が指定されていれば、メッセージを受信して、処理を実行します。

    if (!rconn_is_alive(ofconn->rconn)) {
        ofconn_destroy(ofconn);
    } else if (!rconn_is_connected(ofconn->rconn)) {
        ofconn_flush(ofconn);
    }
}

Controllerとの接続が切れていた時の処理をするのがこのあたり。

OpenFlow メッセージの処理

OpenFlow のメッセージを処理するのは、ofproto/ofproto.c の handle_openflow 関数ですが、実際にはそこから handle_openflow__ 関数を呼び出していて、handle_openflow__ 関数でメッセージの種類毎に処理する関数に割り振っています。

static enum ofperr
handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
{
    const struct ofp_header *oh = msg->data;
    enum ofptype type;
    enum ofperr error;

    error = ofptype_decode(&type, oh);
    if (error) {
        return error;
    }

    switch (type) {
        /* OpenFlow requests. */
    case OFPTYPE_ECHO_REQUEST:
        return handle_echo_request(ofconn, oh);

    case OFPTYPE_FEATURES_REQUEST:
        return handle_features_request(ofconn, oh);

    case OFPTYPE_GET_CONFIG_REQUEST:
        return handle_get_config_request(ofconn, oh);

    case OFPTYPE_SET_CONFIG:
        return handle_set_config(ofconn, oh);

    (略)
}

}

それぞれのメッセージをどのように処理しているのかは、それぞれのメッセージに対応する関数を読んでいけばよいということになります。たとえば、OFPTYPE_ECHO_REQUEST のメッセージは handle_echo_request 関数で処理されます。

static enum ofperr
handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
{
    ofconn_send_reply(ofconn, make_echo_reply(oh));
    return 0;
}

echo reply を作って送るという処理をしていることが確認できます。



このエントリーをはてなブックマークに追加