[Linux] Using RDS with ipv6 Networks

原文はこちら。
https://blogs.oracle.com/linuxkernel/using-rds-with-ipv6-networks

RDSとは、Oracleが開発し、Linuxカーネルに寄贈したオープンソースのReliable Datagram Socketsプロトコルです。カーネル開発者のKa-Cheong Poonが、RDSをipv6対応させるための彼の作業をまとめています。

現在、RDSはIPv4アドレスを使用するピア間でしか動作できません。IPv6が世界中で普及しているので、IPv6アドレスを使用するピア間でRDSを動作させる必要性がますます高まっています。その要求に対応するため、Oracle Linux RDSをアップデートし、IPv6をサポートするようにました。この記事では、RDSを利用するC言語で書かれた既存のアプリケーションを、IPv6をサポートするように変更する方法について説明します。基本的なIPv6 APIについては、RFC 3493を参照してください。RFC 4291では、IPv6アドレス指定アーキテクチャについて説明しています。また、rds-toolsパッケージもアップデートして、IPv6をサポートするようにしました。いくつかの使用例もご紹介します。
RDS Wire Specification 3.1
https://oss.oracle.com/projects/rds/dist/documentation/rds-3.1-spec.htmlState of IPv6 Deployment 2017
https://www.internetsociety.org/resources/doc/2017/state-of-ipv6-deployment-2017/Basic Socket Interface Extensions for IPv6
https://tools.ietf.org/html/rfc3493IP Version 6 Addressing Architecture
https://tools.ietf.org/html/rfc4291
RDSのIPv6サポートは、アプリケーションに対する最小限の変更でIPv6をサポートするよう設計されています。前述のように、次の呼び出しはRDSソケットを作成します。
int sd;
sd = socket(AF_RDS, SOCK_SEQPACKET, 0);
作成されたソケットは、IPv4またはIPv6アドレスを使用しピアとの通信に使用できます。これは、ソケットが作成時にアドレスファミリの指定が必要なTCPソケットとは少し異なります。では、いつこのRDSソケットをIPv6 RDSソケットに変更するのでしょうか?この説明の前に、アドレス処理の説明を簡単にするために、以下のような共用体を定義しましょう。
union sockaddr_ip {
    struct sockaddr_in      addr4;
    struct sockaddr_in6     addr6;
};
この共用体は、IPv4アドレスまたはIPv6アドレスのいずれかを格納するために使用できます。アプリケーションがユーザーが提供するローカルアドレスでユーザーが提供するピアアドレスと通信する必要がある場合、次のコードを使って提供されたバッファーを解析できます。
char *user_suuplied_laddr, *user_supplied_paddr;
struct addrinfo *ainfo;
union sockaddr_ip local_addr, peer_addr;
socklen_t local_addrlen, peer_addrlen;

if (getaddrinfo(user_supplied_laddr, NULL, NULL, &ainfo) != 0) {
    /* Error handling code */
} else {
    /* Just use the first one returned. */
    local_addrlen = ainfo->ai_addrlen;
    memcpy(&local_addr, ainfo->ai_addr, local_addrlen);
    freeaddrinfo(ainfo);
    ...
}
if (getaddrinfo(user_supplied_paddr, NULL, NULL, &ainfo) != 0) {
    /* Error handling code */
} else {
    /* Just use the first one returned. */
    peer_addrlen = ainfo->ai_addrlen;
    memcpy(&dst_addr, ainfo->ai_addr, peer_addrlen);
    freeaddrinfo(ainfo);
    ...
}
/* The following checks for address family mismatched.  Note that the
 * address family field of all socket address structures are at the same
 * position.  Hence we can use sin_family to do the check.
 */
if (local_addr.addr4.sin_family != peer_addr.addr4.sin_family) {
    /* Error handling code */
}
getaddrinfo(3)関数は、IPv4とIPv6の両方のアドレスを認識し、正しい情報でアドレスを埋めます。

ピアと通信する前に、まずRDSソケットをバインドする必要があります。これは以下のように行います。
if (bind(sd, (struct sockaddr *)local_addr, local_addrlen) != 0) {
    /* Error handling code */
}
ユーザーがIPv4アドレスを指定した場合、RDSソケットはIPv4 RDSソケットになり、ユーザーがIPv6アドレスを提供した場合、RDSソケットはIPv6ソケットになります。したがって、RDSソケットのファミリはバインド時に決まります。また、RDSソケットは一度しかバインドできないことにご注意ください。(そのため) 2回目のbind()は失敗します。これは、一度RDSソケットのアドレスファミリを設定すると、変更できないことを意味します。また、RDSソケットは同じファミリのピアとしか通信できません。これは上記のアドレスファミリーのチェックで不一致になる理由です。

(これで)アプリケーションは、以下のようにピアと対話できるようになりました。
char *msg;
size_t msg_len;

if (sendto(sd, msg, msg_len, 0, (struct sockaddr *)peer_addr, peer_addrlen) < 0) {
    /* Error handling code */
}
アプリケーションが同じソケットを使い別のピアと通信することもできます。 新しいピアアドレスがバインドされたアドレスと同じファミリでなければならないことに注意してください。前述のように、getsockname()を使用してソケットにバインドされたアドレスを取得できます。
union sockaddr_ip ipaddr;
socklen_t addrlen;

if (getsockname(sd, (struct sockaddr *)ipaddr, &addrlen) < 0) {
    /* Error handling code */
}
ソケットがまだバインドされていない場合、返されるアドレスファミリはAF_UNSPECです。これは、アドレスがバインド済みか否かの判断に使用できます。ソケットはIPv4アドレスまたはIPv6アドレスのいずれかにバインドできるため、ここではユニオンsockaddr_ip共用体の使用に注意してください。

まとめると、IPv6アドレスを使用するようにRDSアプリケーションを変更するには、IPアドレスを格納するために使用されるソケットアドレス構造とそのソケットアドレス構造を格納するために使用するコードを変更する必要があります。 アドレス構造体を変更すると、他のソケット呼び出しはすべて以前とほぼ同じです。

rds-tools package

rds-toolsパッケージには、rds-info、rds-ping、rds-stressという3つのツールが含まれており、これらもIPv6をサポートするようにアップデートされています。以下はその使用例です。

rds-ping

ホストAでは
[host-a]> ip addr show ib0
9: ib0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 2044 qdisc pfifo_fast state UP qlen 4096
    link/infiniband 80:00:02:08:fe:80:00:00:00:00:00:00:00:02:c9:03:00:0a:79:fd brd 00:ff:ff:ff:ff:12:40:1b:ff:ff:00:00:00:00:00:00:ff:ff:ff:ff
    inet6 2010:211::12/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::202:c903:a:79fd/64 scope link
       valid_lft forever preferred_lft forever
そしてホストBでは
[host-b]> ip addr show ib0
6: ib0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 2044 qdisc pfifo_fast state UP qlen 4096
    link/infiniband 80:00:02:08:fe:80:00:00:00:00:00:00:00:02:c9:03:00:0a:75:a5 brd 00:ff:ff:ff:ff:12:40:1b:ff:ff:00:00:00:00:00:00:ff:ff:ff:ff
    inet6 2010:211::22/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::202:c903:a:75a5/64 scope link
       valid_lft forever preferred_lft forever
であると仮定します。
以下のように、rds-pingを使って両ホスト間のIPv6でのRDSの到達可能性をテストできます。
[host-a]# rds-ping fe80::202:c903:a:75a5%ib0
   1: 160 usec
   2: 155 usec
   ...
fe80::202:c903:a:75a5 はホストBのIPv6リンクローカルアドレスです。リンクローカルアドレスはインターフェイスに関連付ける必要があるため、アドレスの後に%ib0(またはこの場合は%9)を配置して、ib0を使用してそのリンクローカルアドレスに到達するように指定する必要があります。 ホストBからも可能です。
[host-b]# rds-ping 2010:211::12
   1: 162 usec
   2: 153 usec
...
この例では、ホストAのグローバルアドレスをテストしています。IPv6グローバルアドレスの場合インターフェースを指定する必要はありません。

rds-info

rds-infoのデフォルト出力には変更ありません。これは、下位互換性を保つためにIPv6接続情報がデフォルトで表示されないことを意味します。IPv6接続情報を表示するには、「-a」オプションを使用します。 このオプションで、IPv4とIPv6の両方の情報を表示します。rds-pingの例を引き続き使用することにしますが、ホストAで上記を行った後、rds-infoを使用してRDSの接続状態を確認できます
[host-a]# /tmp/rds-info -Ina
RDS IB Connections:
            LocalAddr            RemoteAddr  Tos  SL         LocalDev        RemoteDev
         2010:211::12          2010:211::22    0   0      fe80::2:c903:a:79fd      fe80::2:c903:a:75a5
    fe80::202:c903:a:79fd     fe80::202:c903:a:75a5    0   0      fe80::2:c903:a:79fd      fe80::2:c903:a:75a5
 
RDS Connections:
            LocalAddr            RemoteAddr  Tos   NextTX   NextRX Flgs
         2010:211::12          2010:211::22    0    5    5 --C-
    fe80::202:c903:a:79fd     fe80::202:c903:a:75a5    0       11       11 --C-
上図は、ホストAに2つのRDS接続があることを示しています(1つはホストAとBのグローバルアドレス間の接続、もう1つはホストAとBのリンクローカルアドレス間の接続)。InfiniBandインターフェイスを使用しているため、2つのIB接続もあります。IPv6情報のみが存在するため、"-a"オプションを指定しないと何も表示されません。

rds-stress

アドレスオプション "-s"と "-r"で、IPv6アドレスをとることができるようになりました。 以下は利用例です。
[host-a]> rds-stress -r 2010:211::12
waiting for incoming connection on 2010:211::12:4000
accepted connection from 2010:211::22::41735
negotiated options, tasks will start in 2 seconds
Starting up....
  ...
[host-b]> rds-stress -r 2010:211::22 -s 2010:211::12
connecting to 2010:211::12:4000
negotiated options, tasks will start in 2 seconds
Starting up....
...
アップデートされたrds-stressは新旧rds-stressのいずれとも通信できます。

0 件のコメント:

コメントを投稿