Open vSwitchのソースコードを読む(8) OpenFlow channel の管理
2013-06-18 by Daisuke KotaniConnection 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 を作って送るという処理をしていることが確認できます。
- Open vSwitchのソースコードを読む(11) flow table のエントリ
- Open vSwitchのソースコードを読む(10) flow table の検索
- Open vSwitchのソースコードを読む(9) dpif
- Open vSwitchのソースコードを読む(7) ovs-vswitchd の ofproto
- Open vSwitchのソースコードを読む(6) ovs-vswitchd の bridge
Tweet