[ TOP | Recently ]

2008-07-22 recvfromto, sendfromto.

仕事で使うので、NetBSD で IP_PKTINFO を使えるようにした

そもそもこれはIPV6_PKTINFOを真似して、linuxに実装されたものなのかな。
何をするものかというと、UDP 送信時に src address を指定する方法である。

                §

UDPの送受信に関しては、いろいろとめんどくさいことがよく起きる。
例えば昔のbindでよく問題になっていたのが、ある IP address に問い合わせたdns queryの返事が、
違う IP address から帰ってくる現象。

server が複数の IP address を持っている場合、普通に sendto(2) すると、
ルーティングに従って決定された出力ネットワークインターフェイスに付けられているアドレスが
src address に使われてしまうからだ。

これを回避するためには、INADDR_ANY で bind せずに、ネットワークインターフェイスに
付けられているアドレス毎にソケットを作り、bind しなければならない。
alias address が付いていた場合は、さらにそれぞれのアドレスで同じことをする。
これは、ネットワークインターフェイスやaliasが何十個もあるシステムだと、
socket を何十個も作らなければならないという悲惨なことが起きる。

さすがにこれはアホなので、BSD には IP_RECVDSTADDR というソケットオプションがあり、
INADDR_ANY で bind したソケットでも setsockopt IP_RECVDSTADDR することで、
パケット受信時に、送信先アドレスが拾えるようになる。
これには recvfrom(2) ではなく recvmsg(2) を使う。
送受信アドレスを取得できるということは、つまり recvfrom() ではなく、
recvfromto() が実現できるようになるということだ。

しかしこれでもまだ問題がある。受信時の送信先アドレスがわかったので、そのアドレスを src address にして相手に返事を返したくとも、
INADDR_ANY で bind された socket からだと送信元アドレスを選択することができないのである。
しょうがないので BSD では、返事を返す瞬間だけ socket を作って、送信元にしたいアドレスで bind して送ったりする。

やっぱりこれもアホなので、IPv6 では IPV6_PKTINFO というオプションがあり、
sendmsg(2) する時に送信元アドレスを指定できるようになっている。
つまり sendto(2) では不十分なので sendfromto() ができるように、ということだ。
これらは RFC3542 - Advanced Sockets Application Program Interface (API) for IPv6 で詳しく説明されている。

                §

というわけで、冒頭のパッチは、IPv4 でも IPV6_PKTINFO のように送信元アドレスを指定できるようにするパッチである。
これで送信時も送信元アドレスが指定できるようになり、IPv4 でも sendfromto() 相当が実現できることになる。

これはLinuxには既に実装されている。が、Linux ではIPv4の受信時の送信先アドレスの取得方法がRFC2292(obsolete)
ベースの方法なのでなんだかちぐはぐである。

まとめ。

BSD/LinuxでのUDP送受信時のアドレス取得/指定方法
 受信時の送信先アドレス取得方法送信時の送信元アドレス指定方法
IPv4 (*BSD)setsockopt(IP_RECVDSTADDR) しておいてrecvmsg(2)bind(2)のみ
IPv4 (Linux)setsockopt(IP_PKTINFO) しておいてrecvmsg(2)IP_PKTINFOコントロールを指定して
sendmsg(2)
IPv6 (*BSD)setsockopt(IPV6_RECVDSTADDR) しておいてrecvmsg(2) (deprecated API)

setsockopt(IPV6_PKTINFO) しておいてrecvmsg(2) (obsolete RFC2292)

setsockopt(IPV6_RECVPKTINFO) しておいてrecvmsg(2) (RFC3542)
IPV6_PKTINFOコントロールを指定して
sendmsg(2)
IPv6 (Linux)setsockopt(IPV6_PKTINFO) しておいてrecvmsg(2) (obsolete RFC2292)

setsockopt(IPV6_RECVPKTINFO) しておいてrecvmsg(2) (RFC3542)
IPV6_PKTINFOコントロールを指定して
sendmsg(2)
IPv4 (NetBSD、パッチ適用後)setsockopt(IP_RECVDSTADDR) しておいてrecvmsg(2)
    or
setsockopt(IP_RECVPKTINFO) しておいてrecvmsg(2)
    or
setsockopt(IP_PKTINFO) しておいてrecvmsg(2) (linux compatible API)
IP_PKTINFOコントロールを指定して
sendmsg(2)

というわけで、recvfromto()、sendfromto() の実装例サンプルプログラム。(参考: racoonのソース)


EOF