プログラミングインタフェース

Stream Control Transmission Protocol (SCTP)

ストリーム制御伝送プロトコル (SCTP) は、TCP が提供するサービスと同様のサービスを提供する信頼性の高いトランスポートプロトコルです。さらに、SCTP はネットワークレベルの耐障害性を提供します。SCTP では、関連付けの両端でマルチホーミングがサポートされます。SCTP ソケット API は、TCP にならった 1 対 1 ソケットスタイルをサポートします。その他に SCTP ソケット API は、シグナリングで使用するために設計された 1 対多ソケットスタイルもサポートします。1 対多ソケットスタイルは、プロセスで使用されるファイル記述子の数を削減します。SCTP 関数の呼び出しを使用するには、libsctp ライブラリをリンクする必要があります。

SCTP 関連付けは 2 つの終端の間で設定します。各終端は、特定のタイプのサービス拒否 (DoS) 攻撃から保護するために、cookie による 4 ウェイハンドシェークを使用します。複数の IP アドレスによって終端を表現できます。

SCTP スタックの実装

この節では、IETF 勧告の Stream Control Transmission Protocol (RFC 2960) および Stream Control Transmission Protocol Checksum Change (RFC 3309) について、Solaris における実装の詳細を表に示します。ここでは、RFC 2960 勧告の実装例外について次の表に示します。Solaris オペレーティングシステムの SCTP プロトコルは、RFC 3309 のすべてと、RFC 2960 のセクションのうちこの表に明記されていないセクションのすべてを実装しています。

表 8–3 RFC 2960 との比較における Solaris SCTP 実装の例外

RFC 2960 のセクション 

Solaris 実装における例外 

3. SCTP Packet Format 

3.2 Chunk Field Descriptions: Solaris SCTP は、省略可能な ECNE および CWR を実装していません。 

3.3.2: Solaris SCTP は、Initiation (INIT) の Optional ECN、Host Name Address、および Cookie Preserving パラメータを実装していません。 

3.3.3: Solaris SCTP は、Initiation Acknowledgement、Optional ECN、および Host Name Address パラメータを実装していません。 

5. Association Initialization 

5.1.2, Handle Address Parameters: セクション (B) の Optional Host Name は実装されていません。 

6. User Data Transfer 

6.8, Adler-32 Checksum Calculation: RFC 3309 によって、このセクションは廃止されています。 

10. Interface with Upper Layer 

Solaris SCTP は、IETF TSVWG SCTP ソケット API ドラフトを実装しています。 


注 –

TSVWG SCTP ソケット API の Solaris 10 実装は、Solaris 10 が最初に出荷されたときに公開されていた API ドラフトのバージョンに基づいています。


SCTP ソケットインタフェース

socket() 呼び出しは、IPPROTO_SCTP のソケットを作成するとき、SCTP 固有のソケット作成ルーチンを呼び出します。SCTP ソケットでソケットを呼び出すと、自動的に適切な SCTP ソケットルーチンが呼び出されます。1 対 1 ソケットの場合、各ソケットは 1 つの SCTP 関連付けに対応します。1 対 1 ソケットを作成するには、次の関数を呼び出します。

socket(AF_INET[6], SOCK_STREAM, IPPROTO_STCP);

1 対多スタイルソケットの場合、各ソケットは複数の SCTP 関連付けを処理します。各関連付けには、sctp_assoc_t と呼ばれる関連付け識別子があります。1 対多ソケットを作成するには、次の関数を呼び出します。

socket(AF_INET[6], SOCK_SEQPACKET, IPPROTO_STCP);

sctp_bindx()

int sctp_bindx(int sock, void *addrs, int addrcnt, int flags);

sctp_bindx() 関数は、SCTP ソケット上のアドレスを管理します。sock パラメータが IPv4 ソケットである場合、sctp_bindx() 関数に渡されるアドレスは IPv4 アドレスである必要があります。sock パラメータが IPv6 ソケットである場合、sctp_bindx() 関数に渡されるアドレスは IPv4 アドレスまたは IPv6 アドレスのどちらかになります。sctp_bindx() 関数に渡されるアドレスが INADDR_ANY または IN6ADDR_ANY であると、ソケットは使用可能なすべてのアドレスにバインドします。bind(3SOCKET) を使用して SCTP 終端をバインドします。

*addrs パラメータの値は、1 つ以上のソケットアドレスの配列へのポインタです。各アドレスは、それぞれ該当する構造体に含まれます。アドレスが IPv4 アドレスである場合、sockaddr_in 構造体または sockaddr_in6 構造体に含まれます。IPv6 アドレスである場合、sockaddr_in6 構造体に含まれます。アドレスタイプ群によって、アドレス長が異なります。呼び出し元は、配列内のアドレスの数を addrcnt パラメータによって指定します。

sctp_bindx() 関数は成功すると 0 を返します。失敗すると、sctp_bindx() 関数は -1 を返し、errno の値を該当するエラーコードに設定します。

各ソケットアドレスに同じポートが指定されていない場合、sctp_bindx() 関数は失敗し、errno の値を EINVAL に設定します。

flags パラメータは、現在定義されている次のフラグの 0 以上に対してビット単位の論理和演算を実行することによって求められます。

SCTP_BINDX_ADD_ADDR は、指定されたアドレスを関連付けに追加するように SCTP に指示します。SCTP_BINDX_REM_ADDR は、指定されたアドレスを関連付けから削除するように SCTP に指示します。この 2 つのフラグは相互に排他です。両方が指定された場合、sctp_bindx() は失敗して errno の値を EINVAL に設定します。

呼び出し元は、関連付けからすべてのアドレスを削除することはできません。このような試みは拒否され、sctp_bindx() 関数は失敗して errno の値が EINVAL に設定されます。アプリケーションは、bind() 関数を呼び出したあとに sctp_bindx(SCTP_BINDX_ADD_ADDR) を使用することにより、追加のアドレスを終端に関連付けることができます。またアプリケーションは、sctp_bindx(SCTP_BINDX_REM_ADDR) を使用することにより、待機しているソケットに関連付けられているアドレスを削除できます。アドレスを削除するために sctp_bindx(SCTP_BINDX_REM_ADDR) を使用したあとは、新しい関連付けを受け入れても、削除されたアドレスは再び関連付けられません。終端が動的アドレスをサポートする場合、SCTP_BINDX_REM_ADDR または SCTP_BINDX_ADD_ADDR を使用すると、ピアにメッセージが送信されてピアのアドレスリストが変更されます。接続されている関連付けにおけるアドレスの追加および削除は、オプションの機能です。この機能をサポートしない実装では、EOPNOTSUPP が返されます。

アドレス群が AF_INET または AF_INET6 ではない場合、sctp_bindx() 関数は失敗して EAFNOSUPPORT を返します。sctp_bindx() に渡される sock パラメータのファイル記述子が無効である場合、sctp_bindx() 関数は失敗して EBADF を返します。

sctp_opt_info()

int sctp_opt_info(int sock, sctp_assoc_id_t id, int opt, void *arg, socklen_t *len);

sctp_opt_info() 関数は、sock パラメータに記述されるソケットに関連付けられている SCTP レベルのオプションを返します。1 対多スタイルの SCTP ソケットの場合、id パラメータの値は特定の関連付けを指します。1 対 1 スタイルの SCTP ソケットの場合、id パラメータは無視されます。opt パラメータの値は、取得する SCTP ソケットオプションを指定します。arg パラメータの値は、呼び出し元プログラムによって割り当てられるオプション固有の構造体バッファーです。*len パラメータの値は、オプションの長さです。

opt パラメータは、次の値を取ることができます。

SCTP_RTOINFO

調整可能な再伝送タイムアウト(RTO) を初期化および設定するために使用されるプロトコルのパラメータを返します。プロトコルのパラメータは次の構造体を使用します。

struct sctp_rtoinfo {
    sctp_assoc_t    srto_assoc_id;
    uint32_t        srto_initial;
    uint32_t        srto_max; 
    uint32_t        srto_min;
};
srto_assoc_id

対象になる関連付けを指定するこの値は、呼び出し元プログラムが提供します。

srto_initial

この値は初期 RTO 値です。

srto_max

この値は最大 RTO 値です。

srto_min

この値は最小 RTO 値です。

SCTP_ASSOCINFO

関連付け固有のパラメータを返します。パラメータは次の構造体を使用します。

struct sctp_assocparams {
    sctp_assoc_t    sasoc_assoc_id;
    uint16_t        sasoc_asocmaxrxt;
    uint16_t        sasoc_number_peer_destinations;
    uint32_t        sasoc_peer_rwnd;
    uint32_t        sasoc_local_rwnd;
    uint32_t        sasoc_cookie_life;
};
sasoc_assoc_id

対象になる関連付けを指定するこの値は、呼び出し元プログラムが提供します。

sasoc_assocmaxrxt

この値は、関連付けに対する再伝送の最大数を指定します。

sasoc_number_peer_destinations

この値は、ピアが持つアドレスの数を指定します。

sasoc_peer_rwnd

この値は、ピアの受信ウィンドウの現在値を指定します。

sasoc_local_rwnd

この値は、ピアの送信先の、最後に報告された受信ウィンドウを指定します。

sasoc_cookie_life

この値は、関連付けの cookie の存続時間を指定し、cookie を発行する際に使用されます。

時間の値を使用するすべてのパラメータはミリ秒単位です。

SCTP_DEFAULT_SEND_PARAM

sendto(3SOCKET) 関数の呼び出しが関連付けに関して使用するパラメータのデフォルトセットを返します。パラメータは次の構造体を使用します。

struct sctp_sndrcvinfo {
    uint16_t        sinfo_stream;
    uint16_t        sinfo_ssn;
    uint16_t        sinfo_flags;
    uint32_t        sinfo_ppid;
    uint32_t        sinfo_context;
    uint32_t        sinfo_timetolive;
    uint32_t        sinfo_tsn;
    uint32_t        sinfo_cumtsn;
    sctp_assoc_t    sinfo_assoc_id;
};
sinfo_stream

この値は、sendmsg() 呼び出しのデフォルトストリームを指定します。

sinfo_ssn

この値は常に 0 です。

sinfo_flags

この値は、sendmsg() 呼び出しのデフォルトフラグです。フラグは次の値を取ることができます。

  • MSG_UNORDERED

  • MSG_ADDR_OVER

  • MSG_ABORT

  • MSG_EOF

  • MSG_PR_SCTP

sinfo_ppid

この値は、sendmsg() 呼び出しのデフォルトのペイロードプロトコル識別子です。

sinfo_context

この値は、sendmsg() 呼び出しのデフォルトコンテキストです。

sinfo_timetolive

この値は、ミリ秒単位で期間を指定します。この期間を経過すると、伝送が開始されていないメッセージは期限切れになります。値が 0 の場合は、メッセージが期限切れにならないことを示します。MSG_PR_SCTP フラグが設定されている場合、sinfo_timetolive に指定された期間内に伝送が正常に完了しないメッセージは期限切れになります。

sinfo_tsn

この値は常に 0 です。

sinfo_cumtsn

この値は常に 0 です。

sinfo_assoc_id

この値は、呼び出し元アプリケーションによって設定され、対象になる関連付けを指定します。

SCTP_PEER_ADDR_PARAMS

指定されたピアのアドレスのパラメータを返します。パラメータは次の構造体を使用します。

struct sctp_paddrparams {
    sctp_assoc_t               spp_assoc_id;
    struct sockaddr_storage    spp_address;
    uint32_t                   spp_hbinterval;
    uint16_t                   spp_pathmaxrxt;
};
spp_assoc_id

対象になる関連付けを指定するこの値は、呼び出し元プログラムが提供します。

spp_address

この値は、対象になるピアのアドレスを指定します。

spp_hbinterval

この値は、ミリ秒単位でハートビート間隔を指定します。

spp_pathmaxrxt

この値は、あるアドレスを到達不能と見なすまでの、再伝送を試みる最大回数を指定します。

SCTP_STATUS

関連付けに関する現在の状態情報を返します。パラメータは次の構造体を使用します。

struct sctp_status {
    sctp_assoc_t             sstat_assoc_id;
    int32_t                  sstat_state;
    uint32_t                 sstat_rwnd;
    uint16_t                 sstat_unackdata;
    uint16_t                 sstat_penddata;
    uint16_t                 sstat_instrms;
    uint16_t                 sstat_outstrms;
    uint32_t                 sstat_fragmentation_point;
    struct sctp_paddrinfo    sstat_primary;
};
sstat_assoc_id

対象になる関連付けを指定するこの値は、呼び出し元プログラムが提供します。

sstat_state

この値は、関連付けの現在の状態を示し、関連付けは次の状態を取ることができます。

SCTP_IDLE

SCTP 終端がいずれの関連付けとも関連付けられていません。socket() 関数が終端を開いた直後、または、終端が閉じられた直後、終端はこの状態になります。

SCTP_BOUND

bind() の呼び出しのあと、SCTP 終端が 1 つ以上のローカルアドレスにバインドされています。

SCTP_LISTEN

リモート SCTP 終端からの関連付け要求を終端が待機しています。

SCTP_COOKIE_WAIT

SCTP 終端が INIT チャンクを送信し、INIT-ACK チャンクを待機しています。

SCTP_COOKIE_ECHOED

SCTP 終端が、ピアの INIT-ACK チャンクから受信した cookie をピアにエコーバックしました。

SCTP_ESTABLISHED

SCTP 終端はピアとデータを交換できます。

SCTP_SHUTDOWN_PENDING

SCTP 終端が上位層から SHUTDOWN プリミティブを受信しました。この終端は上位層からデータを受け入れません。

SCTP_SHUTDOWN_SEND

SCTP_SHUTDOWN_PENDING 状態にあった SCTP 終端が SHUTDOWN チャンクをピアに送信しました。SHUTDOWN チャンクが送信されるのは、終端からピアへの未処理データがすべて確認されたあとだけです。終端のピアが SHUTDOWN ACK チャンクを送信すると、終端は SHUTDOWN COMPLETE チャンクを送信し、関連付けが閉じられたと見なされます。

SCTP_SHUTDOWN_RECEIVED

SCTP 終端がピアから SHUTDOWN チャンクを受信しました。この終端はそのユーザーから新しいデータを受け入れません。

SCTP_SHUTDOWN_ACK_SEND

SCTP_SHUTDOWN_RECEIVED 状態の SCTP 終端がピアに SHUTDOWN ACK チャンクを送信しました。終端が SHUTDOWN ACK チャンクを送信するのは、その終端からのすべての未処理データをピアが確認したあとだけです。終端のピアが SHUTDOWN COMPLETE チャンクを送信すると、関連付けは閉じられます。

sstat_rwnd

この値は、関連付けピアの現在の受信ウィンドウです。

sstat_unackdata

この値は、未確認 DATA チャンクの数です。

sstat_penddata

この値は、受信を待機している DATA チャンクの数です。

sstat_instrms

この値は、着信ストリームの数です。

sstat_outstrms

この値は、発信ストリームの数です。

sstat_fragmentation_point

メッセージ、SCTP ヘッダー、および IP ヘッダーを含めたサイズが sstat_fragmentation_point の値を超える場合、メッセージは分割されます。この値は、パケットの着信先アドレスのパス最大伝送ユニット (P-MTU) と同じです。

sstat_primary

この値は、プライマリピアのアドレスに関する情報です。この情報は次の構造体を使用します。

struct sctp_paddrinfo {
    sctp_assoc_t               spinfo_assoc_id;
    struct sockaddr_storage    spinfo_address;
    int32_t                    spinfo_state;
    uint32_t                   spinfo_cwnd;
    uint32_t                   spinfo_srtt;
    uint32_t                   spinfo_rto;
    uint32_t                   spinfo_mtu;
};
spinfo_assoc_id

対象になる関連付けを指定するこの値は、呼び出し元プログラムが提供します。

spinfo_address

この値は、プライマリピアのアドレスです。

spinfo_state

この値は、SCTP_ACTIVE または SCTP_INACTIVE の 2 つの値のいずれかを取ります。

spinfo_cwnd

この値は、ピアアドレスの輻輳ウィンドウです。

spinfo_srtt

この値は、ピアアドレスに関する現在の平滑化された RTT の計算値です。ミリ秒単位で示されます。

spinfo_rto

この値は、ピアアドレスに関する現在の伝送タイムアウト値です。ミリ秒単位で示されます。

spinfo_mtu

この値は、ピアアドレスに関する P-MTU です。

sctp_opt_info() 関数は成功すると 0 を返します。失敗すると、sctp_opt_info() 関数は -1 を返し、errno の値を該当するエラーコードに設定します。sctp_opt_info() に渡される sock パラメータのファイル記述子が無効である場合、sctp_opt_info() 関数は失敗して EBADF を返します。sctp_opt_info() 関数に渡される sock パラメータのファイル記述子がソケットではない場合、sctp_opt_info() 関数は失敗して ENOTSOCK を返します。関連付け ID が 1 対多スタイル SCTP ソケットに対して無効である場合、sctp_opt_info() 関数は失敗して errno の値を EINVAL に設定します。指定されたオプションに対して入力バッファー長が短すぎる場合、sctp_opt_info() 関数は失敗して errno の値を EINVAL に設定します。ピアのアドレスのアドレス群が AF_INET または AF_INET6 ではない場合、sctp_opt_info() 関数は失敗して errno の値を EAFNOSUPPORT に設定します。

sctp_recvmsg()

ssize_t sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from, socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo , int *msg_flags);

sctp_recvmsg() 関数は、s パラメータによって指定される SCTP 終端からのメッセージの受信を有効にします。呼び出し元プログラムによって次の属性を指定できます。

msg

このパラメータは、メッセージバッファーのアドレスです。

len

このパラメータは、メッセージバッファーの長さです。

from

このパラメータは、送信元のアドレスを含むアドレスへのポインタです。

fromlen

from パラメータのアドレスに関連付けられたバッファーのサイズです。

sinfo

このパラメータは、呼び出し元プログラムで sctp_data_io_events が有効な場合のみ有効です。sctp_data_io_events を有効にするには、ソケットオプション SCTP_EVENTS() によって setsockopt 関数を呼び出します。sctp_data_io_events が有効である場合、アプリケーションは着信メッセージごとに sctp_sndrcvinfo 構造体の内容を受信します。このパラメータは、sctp_sndrcvinfo 構造体へのポインタです。構造体はメッセージの受信時にデータを取り込みます。

msg_flags

このパラメータは、存在するメッセージフラグを含みます。

sctp_recvmsg() 関数は、受信するバイト数を返します。エラーが発生した場合、sctp_recvmsg() 関数は -1 を返します。

s パラメータの渡されたファイル記述子が有効でない場合、sctp_recvmsg() 関数は失敗して errno の値を EBADF に設定します。s パラメータに渡されたファイル記述子がソケットでない場合、sctp_recvmsg() 関数は失敗して errno の値を ENOTSOCK に設定します。msg_flags パラメータに値 MSG_OOB が含まれる場合、sctp_recvmsg() 関数は失敗して errno の値を EOPNOTSUPP に設定します。関連付けが確立していない場合、sctp_recvmsg() 関数は失敗して errno の値を ENOTCONN に設定します。

sctp_sendmsg()

ssize_t sctp_sendmsg(int s, const void *msg, size_t len, const struct sockaddr *to, socklen_t tolen, uint32_t ppid, uint32_t flags, uint16_t stream_no, uint32_t timetolive, uint32_t context);

sctp_sendmsg() 関数は、SCTP 終端からメッセージを送信する際の拡張 SCTP 機能を有効にします。

s

この値は、メッセージを送信する SCTP 終端を指定します。

msg

この値は、sctp_sendmsg() 関数によって送信されるメッセージです。

len

この値は、メッセージの長さで、バイト単位で表されます。

to

この値は、メッセージの着信先アドレスです。

tolen

この値は、着信先アドレスの長さです。

ppid

この値は、アプリケーション固有のペイロードプロトコル識別子です。

stream_no

この値は、メッセージのターゲットストリームです。

timetolive

この値は、メッセージが正常にピアに送信されなかった場合に期限切れになるまでの期間で、ミリ秒単位で示されます。

context

メッセージの送信時にエラーが発生した場合に、この値が返されます。

flags

この値は、0 以上の次のフラグビットに対するビット単位の論理和演算を実行することによって求められます。

MSG_UNORDERED

このフラグが設定されている場合 sctp_sendmsg() 関数はメッセージを順不同で配信します。

MSG_ADDR_OVER

このフラグが設定されている場合、sctp_sendmsg() 関数は関連付けのプライマリ着信先アドレスではなく、to パラメータのアドレスを使用します。このフラグは 1 対多スタイル SCTP ソケットの場合にのみ使用されます。

MSG_ABORT

このフラグが設定されている場合、指定された関連付けは ABORT シグナルをピアに送信して異常終了します。このフラグは 1 対多スタイル SCTP ソケットの場合にのみ使用されます。

MSG_EOF

このフラグが設定されている場合、指定された関連付けは適切に終了します。このフラグは 1 対多スタイル SCTP ソケットの場合にのみ使用されます。

MSG_PR_SCTP

このフラグが設定されている場合、timetolive パラメータに指定された期間内に伝送が正常に完了しないメッセージは期限切れになります。

sctp_sendmsg() 関数は、送信したバイト数を返します。エラーが発生した場合、sctp_sendmsg() 関数は -1 を返します。

s パラメータに渡されたファイル記述子が有効でない場合、sctp_sendmsg() 関数は失敗して errno の値を EBADF に設定します。s パラメータの渡されたファイル記述子がソケットでない場合、sctp_sendmsg() 関数は失敗して errno の値を ENOTSOCK に設定します。flags パラメータに値 MSG_OOB が含まれる場合、sctp_sendmsg() 関数は失敗して errno の値を EOPNOTSUPP に設定します。1 対 1 スタイルソケットで flags パラメータに値 MSG_ABORT または MSG_EOF が含まれる場合、sctp_sendmsg() 関数は失敗して errno の値を EOPNOTSUPP に設定します。関連付けが確立していない場合、sctp_sendmsg() 関数は失敗して errno の値を ENOTCONN に設定します。ソケットが停止していてそれ以上の書込みができない場合、sctp_sendmsg() 関数は失敗して errno の値を EPIPE に設定します。ソケットが非ブロックで、伝送待ち行列がいっぱいである場合、sctp_sendmsg() 関数は失敗して errno の値を EAGAIN に設定します。

制御メッセージ長が不正である場合、sctp_sendmsg() 関数は失敗して errno の値を EINVAL に設定します。指定された着信先アドレスが関連付けに属さない場合、sctp_sendmsg() 関数は失敗して errno の値を EINVAL に設定します。stream_no の値が関連付けによってサポートされる発信ストリームの数以外である場合、sctp_sendmsg() 関数は失敗して errno の値を EINVAL に設定します。指定された着信先アドレスのアドレス群が AF_INET または AF_INET6 でない場合、sctp_sendmsg() 関数は失敗して errno の値を EINVAL に設定します。

sctp_send()

ssize_t sctp_send(int s, const void *msg, size_t len, const struct sctp_sndrcvinfo *sinfo , int flags);

sctp_send() 関数は、1 対 1 スタイルソケットと 1 対多スタイルソケットで使用できます。sctp_send() 関数は、SCTP 終端からメッセージを送信する際の拡張 SCTP 機能を有効にします。

s

この値は、socket() 関数によって作成されるソケットです。

msg

この値は、sctp_send() 関数によって送信されるメッセージです。

len

この値は、メッセージの長さで、バイト単位で表されます。

sinfo

この値は、メッセージの送信のために使用されるパラメータです。1 対多スタイルソケットの場合、メッセージの送信先の関連付け ID をこの値に指定できます。

flags

この値は、sendmsg() 関数のフラグパラメータと同じです。

sctp_send() 関数は、送信したバイト数を返します。エラーが発生した場合、sctp_send() 関数は -1 を返します。

s パラメータの渡されたファイル記述子が有効でない場合、sctp_send() 関数は失敗して errno の値を EBADF に設定します。s パラメータの渡されたファイル記述子がソケットでない場合、sctp_send() 関数は失敗して errno の値を ENOTSOCK に設定します。sinfo パラメータの sinfo_flags フィールドに値 MSG_OOB が含まれる場合、sctp_send() 関数は失敗して errno の値を EOPNOTSUPP に設定します。1 対 1 スタイルソケットで sinfo パラメータの sinfo_flags フィールドに値 MSG_ABORT または MSG_EOF が含まれる場合、sctp_send() 関数は失敗して errno の値を EOPNOTSUPP に設定します。関連付けが確立していない場合、sctp_send() 関数は失敗して errno の値を ENOTCONN に設定します。ソケットが停止していてそれ以上の書込みができない場合、sctp_send() 関数は失敗して errno の値を EPIPE に設定します。ソケットが非ブロックで、伝送待ち行列がいっぱいである場合、sctp_send() 関数は失敗して errno の値を EAGAIN に設定します。

制御メッセージ長が不正である場合、sctp_send() 関数は失敗して errno の値を EINVAL に設定します。指定された着信先アドレスが関連付けに属さない場合、sctp_send() 関数は失敗して errno の値を EINVAL に設定します。stream_no の値が関連付けによってサポートされる発信ストリームの数以外である場合、sctp_send() 関数は失敗して errno の値を EINVAL に設定します。指定された着信先アドレスのアドレス群が AF_INET または AF_INET6 ではない場合、sctp_send() 関数は失敗して errno の値を EINVAL に設定します。

分岐関連付け

アプリケーションは、1 対多スタイルソケットで確立された関連付けを別のソケットとファイル記述子に分岐できます。散発的なメッセージの送信側または受信側が多数あり、元の 1 対多スタイルソケットのままである必要があるアプリケーションの場合に、別のソケットとファイル記述子を使用すると便利です。アプリケーションは、大量のデータトラフィックを伝送する関連付けを別のソケット記述子に分岐します。アプリケーションは、sctp_peeloff() 呼び出しを使用して、関連付けを別のソケットに分岐します。新しいソケットは 1 対 1 スタイルソケットです。sctp_peeloff() 関数の構文は次のとおりです。

int sctp_peeloff(int sock, sctp_assoc_t id);
sock

socket() システムコールから返される元の 1 対多スタイルソケット記述子。

id

別のファイル記述子に分岐する関連付けの識別子。

sock に渡されるソケット記述子が 1 対多スタイル SCTP ソケットではない場合、sctp_peeloff() 関数は失敗して EOPTNOTSUPP を返します。id() の値が 0 である場合、または、id の値が sock パラメータに渡されるソケット記述子の関連付けの最大数より大きい場合、sctp_peeloff 関数は失敗して EINVAL を返します。sctp_peeloff() 関数が新しいユーザーファイル記述子またはファイル構造体を作成できない場合、関数は失敗して EMFILE を返します。

sctp_getpaddrs()

sctp_getpaddrs() 関数は、関連付け内のすべてのピアを返します。

int sctp_getpaddrs(int sock, sctp_assoc_t id, void **addrs);

sctp_getpaddrs() 関数が正常に結果を返した場合、**addrs パラメータの値は、動的に割り当てられるパックされた配列である、各アドレスの適切なタイプの sockaddr 構造体を指します。呼び出し元スレッドは、sctp_freepaddrs() 関数によってメモリーを解放します。**addrs パラメータに値 NULL はありません。sock に指定されるソケット記述子が IPv4 ソケット用である場合、sctp_getpaddrs() 関数は IPv4 アドレスを返します。sock に指定されるソケット記述子が IPv6 ソケット用である場合、sctp_getpaddrs() 関数は IPv4 アドレスと IPv6 アドレスを混在して返します。1 対多スタイルソケットの場合、id パラメータは照会する関連付けを指定します。1 対 1 スタイルソケットの場合、sctp_getpaddrs() 関数は id パラメータを無視します。sctp_getpaddrs() 関数が正常に結果を返す場合、関連付け内のピアアドレスの数が返されます。ソケット上に関連付けがない場合、sctp_getpaddrs() 関数は 0 を返し、**addrs パラメータの値は未定義です。エラーが発生した場合、sctp_getpaddrs() 関数は -1 を返し、**addrs パラメータの値は未定義です。

sctp_getpaddrs() に渡される sock パラメータのファイル記述子が無効である場合、sctp_getpaddrs() 関数は失敗して EBADF を返します。sctp_getpaddrs() 関数に渡される sock パラメータのファイル記述子がソケットでない場合、sctp_getpaddrs() 関数は失敗して ENOTSOCK を返します。sctp_getpaddrs() 関数に渡される sock パラメータのファイル記述子が接続されていないソケットである場合、sctp_getpaddrs() 関数は失敗して ENOTCONN を返します。

sctp_freepaddrs()

sctp_freepaddrs() 関数は、それ以前の sctp_getpaddrs() の呼び出しによって割り当てられたすべての資源を解放します。sctp_freepaddrs() 関数の構文は次のとおりです。

void sctp_freepaddrs(void *addrs);

*addrs パラメータは、sctp_getpaddrs() 関数によって返されたピアのアドレスを含む配列です。

sctp_getladdrs()

sctp_getladdrs() 関数は、ソケット上のローカルでバインドされているすべてのアドレスを返します。sctp_getladdrs() 関数の構文は次のとおりです。

int sctp_getladdrs(int sock, sctp_assoc_t id, void **addrs);

sctp_getladdrs() 関数が正常に結果を返した場合、addrs の値は動的に割り当てられるパックされた配列の sockaddr 構造体を指します。sockaddr 構造体は各ローカルアドレスの適切なタイプです。呼び出し元アプリケーションは、sctp_freeladdrs() 関数を使用してメモリーを解放します。addrs パラメータの値が NULL であってはなりません。

sd パラメータによって参照されるソケットが IPv4 ソケットである場合、sctp_getladdrs() 関数は IPv4 アドレスを返します。sd パラメータによって参照されるソケットが IPv6 ソケットである場合、sctp_getladdrs() 関数は必要に応じて IPv4 アドレスと IPv6 アドレスを混在して返します。

1 対多スタイルソケットで sctp_getladdrs() 関数が起動されると、id パラメータは照会する関連付けを指定します。1 対 1 ソケットで sctp_getladdrs() 関数で動作する場合、id パラメータは無視されます。

id パラメータの値が 0 である場合、sctp_getladdrs() 関数は特定の関連付けに関係なくローカルでバインドされているアドレスを返します。sctp_getladdrs() 関数が正常に結果を返す場合、ソケットにバインドされているローカルアドレスの数を報告します。ソケットがバインドされていない場合、sctp_getladdrs() 関数は 0 を返し、*addrs の値は未定義です。エラーが発生した場合、sctp_getladdrs() 関数は -1 を返し、*addrs パラメータの値は未定義です。

sctp_freeladdrs()

sctp_freeladdrs() 関数は、それ以前の sctp_getladdrs() の呼び出しによって割り当てられたすべての資源を解放します。sctp_freeladdrs() 関数の構文は次のとおりです。

void sctp_freeladdrs(void *addrs);

*addrs パラメータは、sctp_getladdrs() 関数によって返されたピアのアドレスを含む配列です。

SCTP を使用したコーディング例

この節では、SCTP ソケットの 2 つの使用法を示します。


例 8–17 SCTP エコークライアント

/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* To enable socket features used for SCTP socket. */
#define _XPG4_2
#define __EXTENSIONS__

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <netinet/sctp.h>
#include <netdb.h>

#define BUFLEN 1024

static void
usage(char *a0)
{
    fprintf(stderr, "Usage: %s <addr>\n", a0);
}

/*
 * Read from the network.
 */
static void
readit(void *vfdp)
{
    int   fd;
    ssize_t   n;
    char   buf[BUFLEN];
    struct msghdr  msg[1];
    struct iovec  iov[1];
    struct cmsghdr  *cmsg;
    struct sctp_sndrcvinfo *sri;
    char   cbuf[sizeof (*cmsg) + sizeof (*sri)];
    union sctp_notification *snp;

    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    fd = *(int *)vfdp;

    /* Initialize the message header for receiving */
    memset(msg, 0, sizeof (*msg));
    msg->msg_control = cbuf;
    msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);
    msg->msg_flags = 0;
    cmsg = (struct cmsghdr *)cbuf;
    sri = (struct sctp_sndrcvinfo *)(cmsg + 1);
    iov->iov_base = buf;
    iov->iov_len = BUFLEN;
    msg->msg_iov = iov;
    msg->msg_iovlen = 1;

    while ((n = recvmsg(fd, msg, 0)) > 0) {
        /* Intercept notifications here */
        if (msg->msg_flags & MSG_NOTIFICATION) {
            snp = (union sctp_notification *)buf;
            printf("[ Receive notification type %u ]\n",
                snp->sn_type);
            continue;
        }
        msg->msg_control = cbuf;
        msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);
        printf("[ Receive echo (%u bytes): stream = %hu, ssn = %hu, "
            "flags = %hx, ppid = %u ]\n", n,
            sri->sinfo_stream, sri->sinfo_ssn, sri->sinfo_flags,
            sri->sinfo_ppid);
    }

    if (n < 0) {
        perror("recv");
        exit(1);
    }

    close(fd);
    exit(0);
}

#define MAX_STREAM 64

static void
echo(struct sockaddr_in *addr)
{
    int     fd;
    uchar_t     buf[BUFLEN];
    ssize_t    n;
    int    perr;
    pthread_t   tid;
    struct cmsghdr   *cmsg;
    struct sctp_sndrcvinfo  *sri;
    char    cbuf[sizeof (*cmsg) + sizeof (*sri)];
    struct msghdr   msg[1];
    struct iovec   iov[1];
    int    ret;
    struct sctp_initmsg   initmsg;
    struct sctp_event_subscribe events;

    /* Create a one-one SCTP socket */
    if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) {
        perror("socket");
        exit(1);
    }

    /*
     * We are interested in association change events and we want
     * to get sctp_sndrcvinfo in each receive.
     */
    events.sctp_association_event = 1; 
    events.sctp_data_io_event = 1; 
    ret = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &events,
        sizeof (events));
    if (ret < 0) {
        perror("setsockopt SCTP_EVENTS");
        exit(1);
    }

    /*
     * Set the SCTP stream parameters to tell the other side when
     * setting up the association.
     */
    memset(&initmsg, 0, sizeof(struct sctp_initmsg));
    initmsg.sinit_num_ostreams = MAX_STREAM;
    initmsg.sinit_max_instreams = MAX_STREAM;
    initmsg.sinit_max_attempts = MAX_STREAM;
    ret = setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,
        sizeof(struct sctp_initmsg));
    if (ret < 0) {
        perror("setsockopt SCTP_INITMSG");
        exit(1);
    }

    if (connect(fd, (struct sockaddr *)addr, sizeof (*addr)) == -1) {
        perror("connect");
        exit(1);
    }

    /* Initialize the message header structure for sending. */
    memset(msg, 0, sizeof (*msg));
    iov->iov_base = buf;
    msg->msg_iov = iov;
    msg->msg_iovlen = 1;
    msg->msg_control = cbuf;
    msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);
    msg->msg_flags |= MSG_XPG4_2;

    memset(cbuf, 0, sizeof (*cmsg) + sizeof (*sri));
    cmsg = (struct cmsghdr *)cbuf;
    sri = (struct sctp_sndrcvinfo *)(cmsg + 1);

    cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*sri);
    cmsg->cmsg_level = IPPROTO_SCTP;
    cmsg->cmsg_type  = SCTP_SNDRCV;

    sri->sinfo_ppid   = 1;
    /* Start sending to stream 0. */
    sri->sinfo_stream = 0;

    /* Create a thread to receive network traffic. */
    perr = pthread_create(&tid, NULL, (void *(*)(void *))readit, &fd);

    if (perr != 0) {
        fprintf(stderr, "pthread_create: %d\n", perr);
        exit(1);
    }

    /* Read from stdin and then send to the echo server. */
    while ((n = read(fileno(stdin), buf, BUFLEN)) > 0) {
        iov->iov_len = n;
        if (sendmsg(fd, msg, 0) < 0) {
            perror("sendmsg");
            exit(1);
        }
        /* Send the next message to a different stream. */
        sri->sinfo_stream = (sri->sinfo_stream + 1) % MAX_STREAM;
    }

    pthread_cancel(tid);
    close(fd);
}

int
main(int argc, char **argv)
{
    struct sockaddr_in addr[1];
    struct hostent *hp;
    int error;

    if (argc < 2) {
        usage(*argv);
        exit(1);
    }

    /* Find the host to connect to. */ 
    hp = getipnodebyname(argv[1], AF_INET, AI_DEFAULT, &error);
    if (hp == NULL) {
        fprintf(stderr, "host not found\n");
        exit(1);
    }

    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = *(ipaddr_t *)hp->h_addr_list[0];
    addr->sin_port = htons(5000);

    echo(addr);

    return (0);
}


例 8–18 SCTP エコーサーバー

/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* To enable socket features used for SCTP socket. */
#define _XPG4_2
#define __EXTENSIONS__

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/sctp.h>

#define BUFLEN 1024

/*
 * Given an event notification, print out what it is.
 */
static void
handle_event(void *buf)
{
    struct sctp_assoc_change *sac;
    struct sctp_send_failed  *ssf;
    struct sctp_paddr_change *spc;
    struct sctp_remote_error *sre;
    union sctp_notification  *snp;
    char    addrbuf[INET6_ADDRSTRLEN];
    const char   *ap;
    struct sockaddr_in  *sin;
    struct sockaddr_in6  *sin6;

    snp = buf;

    switch (snp->sn_header.sn_type) {
    case SCTP_ASSOC_CHANGE:
        sac = &snp->sn_assoc_change;
        printf("^^^ assoc_change: state=%hu, error=%hu, instr=%hu "
            "outstr=%hu\n", sac->sac_state, sac->sac_error,
            sac->sac_inbound_streams, sac->sac_outbound_streams);
        break;
    case SCTP_SEND_FAILED:
        ssf = &snp->sn_send_failed;
        printf("^^^ sendfailed: len=%hu err=%d\n", ssf->ssf_length,
            ssf->ssf_error);
        break;
    case SCTP_PEER_ADDR_CHANGE:
        spc = &snp->sn_paddr_change;
        if (spc->spc_aaddr.ss_family == AF_INET) {
            sin = (struct sockaddr_in *)&spc->spc_aaddr;
            ap = inet_ntop(AF_INET, &sin->sin_addr, addrbuf,
                INET6_ADDRSTRLEN);
        } else {
            sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr;
            ap = inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf,
                INET6_ADDRSTRLEN);
        }
        printf("^^^ intf_change: %s state=%d, error=%d\n", ap,
            spc->spc_state, spc->spc_error);
        break;
    case SCTP_REMOTE_ERROR:
        sre = &snp->sn_remote_error;
        printf("^^^ remote_error: err=%hu len=%hu\n",
            ntohs(sre->sre_error), ntohs(sre->sre_length));
        break;
    case SCTP_SHUTDOWN_EVENT:
        printf("^^^ shutdown event\n");
        break;
    default:
        printf("unknown type: %hu\n", snp->sn_header.sn_type);
        break;
    }
}

/*
 * Receive a message from the network.
 */
static void *
getmsg(int fd, struct msghdr *msg, void *buf, size_t *buflen,
    ssize_t *nrp, size_t cmsglen)
{
    ssize_t  nr = 0;
    struct iovec iov[1];

    *nrp = 0;
    iov->iov_base = buf;
    msg->msg_iov = iov;
    msg->msg_iovlen = 1;

    /* Loop until a whole message is received. */
    for (;;) {
        msg->msg_flags = MSG_XPG4_2;
        msg->msg_iov->iov_len = *buflen;
        msg->msg_controllen = cmsglen;

        nr += recvmsg(fd, msg, 0);
        if (nr <= 0) {
            /* EOF or error */
            *nrp = nr;
            return (NULL);
        }

        /* Whole message is received, return it. */
        if (msg->msg_flags & MSG_EOR) {
            *nrp = nr;
            return (buf);
        }

        /* Maybe we need a bigger buffer, do realloc(). */
        if (*buflen == nr) {
            buf = realloc(buf, *buflen * 2);
            if (buf == 0) {
                fprintf(stderr, "out of memory\n");
                exit(1);
            }
            *buflen *= 2;
        }

        /* Set the next read offset */
        iov->iov_base = (char *)buf + nr;
        iov->iov_len = *buflen - nr;

    }
}

/*
 * The echo server.
 */
static void
echo(int fd)
{
    ssize_t   nr;
    struct sctp_sndrcvinfo *sri;
    struct msghdr  msg[1];
    struct cmsghdr  *cmsg;
    char   cbuf[sizeof (*cmsg) + sizeof (*sri)];
    char   *buf;
    size_t   buflen;
    struct iovec  iov[1];
    size_t   cmsglen = sizeof (*cmsg) + sizeof (*sri);

    /* Allocate the initial data buffer */
    buflen = BUFLEN;
    if ((buf = malloc(BUFLEN)) == NULL) {
        fprintf(stderr, "out of memory\n");
        exit(1);
    }

    /* Set up the msghdr structure for receiving */
    memset(msg, 0, sizeof (*msg));
    msg->msg_control = cbuf;
    msg->msg_controllen = cmsglen;
    msg->msg_flags = 0;
    cmsg = (struct cmsghdr *)cbuf;
    sri = (struct sctp_sndrcvinfo *)(cmsg + 1);

    /* Wait for something to echo */
    while ((buf = getmsg(fd, msg, buf, &buflen, &nr, cmsglen)) != NULL) {

        /* Intercept notifications here */
        if (msg->msg_flags & MSG_NOTIFICATION) {
            handle_event(buf);
            continue;
        }

        iov->iov_base = buf;
        msg->msg_iov = iov;
        msg->msg_iovlen = 1;
        iov->iov_len = nr;
        msg->msg_control = cbuf;
        msg->msg_controllen = sizeof (*cmsg) + sizeof (*sri);

        printf("got %u bytes on stream %hu:\n", nr,
               sri->sinfo_stream);
        write(0, buf, nr);

        /* Echo it back */
        msg->msg_flags = MSG_XPG4_2;
        if (sendmsg(fd, msg, 0) < 0) {
            perror("sendmsg");
            exit(1);
        }
    }

    if (nr < 0) {
        perror("recvmsg");
    }
    close(fd);
}

int
main(void)
{
    int    lfd;
    int    cfd;
    int    onoff = 1;
    struct sockaddr_in  sin[1];
    struct sctp_event_subscribe events;
    struct sctp_initmsg  initmsg;

    if ((lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) {
        perror("socket");
        exit(1);
    }

    sin->sin_family = AF_INET;
    sin->sin_port = htons(5000);
    sin->sin_addr.s_addr = INADDR_ANY;
    if (bind(lfd, (struct sockaddr *)sin, sizeof (*sin)) == -1) {
        perror("bind");
        exit(1);
    }

    if (listen(lfd, 1) == -1) {
        perror("listen");
        exit(1);
    }

    (void) memset(&initmsg, 0, sizeof(struct sctp_initmsg));
    initmsg.sinit_num_ostreams = 64;
    initmsg.sinit_max_instreams = 64;
    initmsg.sinit_max_attempts = 64;
    if (setsockopt(lfd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,
           sizeof(struct sctp_initmsg)) < 0) { 
        perror("SCTP_INITMSG");
        exit (1);
    }

    /* Events to be notified for */
    (void) memset(&events, 0, sizeof (events));
    events.sctp_data_io_event = 1;
    events.sctp_association_event = 1;
    events.sctp_send_failure_event = 1;
    events.sctp_address_event = 1;
    events.sctp_peer_error_event = 1;
    events.sctp_shutdown_event = 1;

    /* Wait for new associations */
    for (;;) {
        if ((cfd = accept(lfd, NULL, 0)) == -1) {
            perror("accept");
            exit(1);
        }

        /* Enable ancillary data */
        if (setsockopt(cfd, IPPROTO_SCTP, SCTP_EVENTS, &events,
               sizeof (events)) < 0) {
            perror("setsockopt SCTP_EVENTS");
            exit(1);
        }
        /* Echo back any and all data */
        echo(cfd);
    }
}