setsockopt/getsockopt+IPPROTO_IPの落とし穴

setsockopt/getsockoptをIPPROTO_IPレベルで利用したときにincludeするファイルとリンクするライブラリの整合性が無いと問題が発生します。 ここでは、発生する問題とその理由を説明したいと思います。

includeファイルとリンクするライブラリの整合性

winsockのバージョン1とバージョン2では、インクルードするヘッダファイルが異なります。 winsockパージョン1では、winsock.hをincludeします。 winsockバージョン2では、winsock2.hをincludeします。

winsockのバージョン1と2では、リンクするライブラリも異なります。 バージョン1では、wsock32.libをリンクします。 バージョン2では、ws2_32.libをリンクします。

includeするwinsockのバージョンと、リンクするライブラリのバージョンの整合性が無い状態でsetsockopt/getsockoptを利用すると、挙動が変になる場合があります。 具体的には、setsockopt/getsockoptをIPPROTO_IPレベルで利用したときに問題が発生します。

IPPROTO_IPオプションの定義の違い

この問題の原因は、winsockとwinsock2でdefineされている値が異なるためです。 winsock version 1で利用されるwinsock.hでは、IPPROTO_IPレベルのオプションは以下のように定義されています。

<<winsock.h>>


/*
 * Options for use with [gs]etsockopt at the IP level.
 */
#define IP_OPTIONS          1     /* set/get IP per-packet options    */
#define IP_MULTICAST_IF     2     /* set/get IP multicast interface   */
#define IP_MULTICAST_TTL    3     /* set/get IP multicast timetolive  */
#define IP_MULTICAST_LOOP   4     /* set/get IP multicast loopback    */
#define IP_ADD_MEMBERSHIP   5     /* add  an IP group membership      */
#define IP_DROP_MEMBERSHIP  6     /* drop an IP group membership      */
#define IP_TTL              7     /* set/get IP Time To Live          */
#define IP_TOS              8     /* set/get IP Type Of Service       */
#define IP_DONTFRAGMENT     9     /* set/get IP Don't Fragment flag   */

winsock version 2では、IPPROTO_IPレベルのオプションはws2tcpip.hファイルで以下のように定義されています。

<<ws2tcpip.h>>


/* Option to use with [gs]etsockopt at the IPPROTO_IP level */

#define IP_OPTIONS                 1 /* set/get IP options */
#define IP_HDRINCL                 2 /* header is included with data */
#define IP_TOS                     3 /* IP type of service and preced*/
#define IP_TTL                     4 /* IP time to live */
#define IP_MULTICAST_IF            9 /* set/get IP multicast i/f  */
#define IP_MULTICAST_TTL          10 /* set/get IP multicast ttl */
#define IP_MULTICAST_LOOP         11 /*set/get IP multicast loopback */
#define IP_ADD_MEMBERSHIP         12 /* add an IP group membership */
#define IP_DROP_MEMBERSHIP        13 /* drop an IP group membership */
#define IP_DONTFRAGMENT           14 /* don't fragment IP datagrams */
#define IP_ADD_SOURCE_MEMBERSHIP  15 /* join IP group/source */
#define IP_DROP_SOURCE_MEMBERSHIP 16 /* leave IP group/source */
#define IP_BLOCK_SOURCE           17 /* block IP group/source */
#define IP_UNBLOCK_SOURCE         18 /* unblock IP group/source */
#define IP_PKTINFO                19 /* receive packet information for ipv4*/
#define IP_RECEIVE_BROADCAST      22 /* allow/block broadcast reception */

IPPROTO_IPオプションの値をwinsock.hとws2tcpip.hで比較すると、IP_OPTIONS以外は全て値が変わってしまっています。 この変更のため、winsock.hをincludeしてws2_32.lib(winsock2用)をリンクすると不整合が発生してしまいます。 (winsock2.h、ws2tcpip.hをincludeしてwsock32.libをリンクしても不整合が発生します。)

この不整合により、リンク後出来上がる実行ファイルではプログラムを書いている時に意図しているオプションではなくなってしまいます。

発生する症状

IPPROTO_IPレベルで発生する問題には、2種類あります。 一つはsetsockopt/getsockoptがエラーコード10042(WSAENOPROTOOPT)で失敗するものです。 もう一つはわかりにくく、setsockopt/getsockoptは成功しますが意図した動作をしません。

setsockopt/getsockoptがWSAENOPROTOOPTで失敗するのは、以下のオプションです。

  • IP_ADD_MEMBERSHIP
  • IP_DROP_MEMBERSHIP
  • IP_TTL
  • IP_TOS

setsockopt/getsockoptが成功し、意図しない動作をするのは以下のオプションです。 この場合はsetsockopt/getsockoptが成功してしまうため、問題の発見が困難になります。

  • IP_MULTICAST_IF
  • IP_MULTICAST_TTL
  • IP_MULTICAST_LOOP
  • IP_DONTFRAGMENT

問題回避方法

この問題を回避するには、利用するヘッダファイルとリンクするライブラリファイルの整合性を正しく保つ必要があります。

プロフェッショナルIPv6解説動画シリーズ再生リスト

動画で学ぶ「プロフェッショナルIPv6」を作っています。 もしよろしければご覧ください。お楽しみいただければ幸いです!