MULTICAST(4) | FreeBSD Kernel Interfaces Manual | MULTICAST(4) |
名称
multicast — マルチキャストルーティング書式
options MROUTING
#include < sys/types.h>
#include < sys/socket.h>
#include < netinet/in.h>
#include < netinet/ip_mroute.h>
#include < netinet6/ip6_mroute.h>
int
getsockopt( int s, IPPROTO_IP, MRT_INIT, void *optval, socklen_t *optlen);
int
setsockopt( int s, IPPROTO_IP, MRT_INIT, const void *optval, socklen_t optlen);
int
getsockopt( int s, IPPROTO_IPV6, MRT6_INIT, void *optval, socklen_t *optlen);
int
setsockopt( int s, IPPROTO_IPV6, MRT6_INIT, const void *optval, socklen_t optlen);
解説
マルチキャストルーティングは、マルチポイントネットワークでデータパケットを効率的に一連のマルチキャストリスナに伝播するために使用されます。ユニキャストがすべてのリスナにデータを複製するために使用されるなら、いくつかのネットワークリンクは、同じデータパケットの複数のコピーを伝達するかもしれません。マルチキャストルーティングで、オーバヘッドは、ネットワークリンク単位で (多くても) 1 つのコピーまで下げられます。すべてのマルチキャスト可能なルータは、一般的なマルチキャストルーティングプロトコルを実行しなければなりません。 Protocol Independent Multicast - スパース (まばらな) モード (PIM-SM)、または、 Protocol Independent Multicast - デンス (濃い) モード (PIM-DM) のいずれかは、現在、インターネットコミュニティで一般的に、受け入れられたプロトコルであるので、これらを使用することを勧めます。 歴史 セクションで、以前のマルチキャストルーティングプロトコルについて議論しています。
マルチキャストルーティングを始めるためには、ユーザは、カーネル (カーネル設定オプションについては、 書式 を参照) でマルチキャスト転送を有効にしなければならなくて、マルチキャストルーティング可能なユーザレベルプロセスを実行しなければなりません。開発者の観点から、 プログラミングガイド セクションで説明されたプログラミングガイドは、カーネルにおけるマルチキャスト転送を制御するために使用されるでしょう。
プログラミングガイド
このセクションは、基本的なマルチキャストルーティング API に関する情報を提供します。いわゆる“高度なマルチキャスト API”は、 高度なマルチキャスト API プログラミングガイド デクションで説明されます。最初に、マルチキャストルーティングソケットがオープンされなければなりません。そのソケットは、カーネルでマルチキャスト転送を制御するために使用されるでしょう。以下でのほとんどの操作がある種の特権 (すなわち、root の特権) を必要とすることに注意してください:
/* IPv4 */ int mrouter_s4; mrouter_s4 = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
int mrouter_s6; mrouter_s6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
IGMP か MLD マルチキャストグループメンバシップメッセージの送信か受信において、IGMP か ICMPv6 ソケット (それぞれ IPv4 と IPv6 の場合) をルータがオープンする必要があるなら、同様に mrouter_s4 か mrouter_s6 ソケットは、それぞれ IGMP か MLD メッセージの送信か受信に使用されるべきであることに注意してください。 BSD 派生カーネルの場合には、IGMP か MLD メッセージのためだけに別々のソケットをオープンできるかもしれません。しかしながら、ある他のカーネル (例えば、 Linux) は、マルチキャストルーティングソケットが IGMP か MLD メッセージの送受信を使用しなければならないことを必要とします。したがって、移植性の理由で、マルチキャストルーティングソケットは、同様に IGMP と MLD メッセージのために再利用すべきです。
マルチキャストルーティングソケットがオープンされた後に、それは、カーネルでマルチキャスト転送を有効にするか無効にするために使用することができます:
/* IPv4 */ int v = 1; /* 1 は、有効, または 0 は、無効 */ setsockopt(mrouter_s4, IPPROTO_IP, MRT_INIT, (void *)&v, sizeof(v));
/* IPv6 */ int v = 1; /* 1 は、有効, または 0 は、無効 */ setsockopt(mrouter_s6, IPPROTO_IPV6, MRT6_INIT, (void *)&v, sizeof(v)); ... /* 必要なら, すべての ICMPv6 メッセージをフィルタする */ struct icmp6_filter filter; ICMP6_FILTER_SETBLOCKALL(&filter); setsockopt(mrouter_s6, IPPROTO_ICMPV6, ICMP6_FILTER, (void *)&filter, sizeof(filter));
マルチキャスト転送が有効にされた後に、マルチキャストルーティングソケットは、 PIM-SM か PIM-DM が実行されているなら、カーネルで PIM 処理を有効にするために使用することができます ( pim(4) 参照)。
マルチキャスト転送に使用される、それぞれのネットワークインタフェース (例えば、物理または仮想トンネル) については、対応するマルチキャストインタフェースがカーネルに追加されなければなりません:
/* IPv4 */ struct vifctl vc; memset(&vc, 0, sizeof(vc)); /* すべての vifctl フィールドを適切に割り当てます */ vc.vifc_vifi = vif_index; vc.vifc_flags = vif_flags; vc.vifc_threshold = min_ttl_threshold; vc.vifc_rate_limit = 0; memcpy(&vc.vifc_lcl_addr, &vif_local_address, sizeof(vc.vifc_lcl_addr)); setsockopt(mrouter_s4, IPPROTO_IP, MRT_ADD_VIF, (void *)&vc, sizeof(vc));
vif_index は、vif 単位でユニークでなければなりません。 vif_flags は、 < netinet/ip_mroute.h> で定義されるように VIFF_* フラグを含んでいます。 VIFF_TUNNEL フラグは、もはや FreeBSD でサポートされません。トンネル上でマルチキャストデータグラムを転送したいユーザは、 gif(4) または gre(4) トンネルを設定して、物理インタフェースとして、それを使用することを考慮すべきです。
min_ttl_threshold は、マルチキャストデータパケットがその vif で転送されるためになければならない最小の TTL (有効期間) を含んでいます。通常、それは、1 の値となります。
max_rate_limit 引数は、もはや FreeBSD でサポートされず、0 に設定されるべきです。マルチキャストデータグラムをレート制限したいユーザは、 dummynet(4) または altq(4) の使用を考慮すべきです。
vif_local_address は、対応するローカルインタフェースのローカル IP アドレスを含んでいます。 vif_remote_address は、DVMRP マルチキャストトンネルの場合にリモート IP アドレスを含んでいます。
/* IPv6 */ struct mif6ctl mc; memset(&mc, 0, sizeof(mc)); /* すべての mif6ctl フィールドを適切に割り当てます */ mc.mif6c_mifi = mif_index; mc.mif6c_flags = mif_flags; mc.mif6c_pifi = pif_index; setsockopt(mrouter_s6, IPPROTO_IPV6, MRT6_ADD_MIF, (void *)&mc, sizeof(mc));
mif_index は、vif 単位でユニークでなければなりません。 mif_flags は、 < netinet6/ip6_mroute.h> で定義されるように MIFF_* を含んでいます。 pif_index は、対応するローカルインタフェースの物理的なインタフェースインデックスです。
マルチキャストインタフェースは、次によって削除されます:
/* IPv4 */ vifi_t vifi = vif_index; setsockopt(mrouter_s4, IPPROTO_IP, MRT_DEL_VIF, (void *)&vifi, sizeof(vifi));
/* IPv6 */ mifi_t mifi = mif_index; setsockopt(mrouter_s6, IPPROTO_IPV6, MRT6_DEL_MIF, (void *)&mifi, sizeof(mifi));
マルチキャスト転送が有効にされて、マルチキャスト仮想インタフェースが追加された後に、カーネルは、以前に MRT_INIT または MRT6_INIT でオープンされているマルチキャストルーティングソケット上で upcall メッセージ (このテキストの後ろでまたの名は、シグナルです) を配信できます。 IPv4 upcall は、フィールド im_mbz を 0 に設定した struct igmpmsg ヘッダ ( < netinet/ip_mroute.h> 参照) があります。このヘッダは、プロトコルフィールド ip_p を 0 に設定した struct ip 構造体が後に続くことに注意してください。 IPv6 upcall は、フィールド im6_mbz を 0 に設定した struct mrt6msg ヘッダ ( < netinet6/ip6_mroute.h> 参照) があります。このヘッダは、次のヘッダフィールド ip6_nxt を 0 に設定した struct ip6_hdr 構造体が後に続くことに注意してください。
upcall ヘッダは、それぞれ IPv4 と IPv6 のための upcall のタイプ IGMPMSG_* と MRT6MSG_* でフィールド im_msgtype と im6_msgtype を含んでいます。 upcall ヘッダフィールドの残りの値と upcall メッセージの本体は、特定の upcall タイプに依存します。
upcall メッセージタイプが IGMPMSG_NOCACHE または MRT6MSG_NOCACHE であるなら、これは、マルチキャストパケットがマルチキャストルータに到達したことを示しますが、ルータには、そのパケットのための転送状態がありません。通常、 upcall は、適切な Multicast Forwarding Cache (MFC) エントリをカーネルにインストールするためにマルチキャストのユーザレベルのプロセスのためのシグナルとなります。
MFC エントリは、次によって追加されます:
/* IPv4 */ struct mfcctl mc; memset(&mc, 0, sizeof(mc)); memcpy(&mc.mfcc_origin, &source_addr, sizeof(mc.mfcc_origin)); memcpy(&mc.mfcc_mcastgrp, &group_addr, sizeof(mc.mfcc_mcastgrp)); mc.mfcc_parent = iif_index; for (i = 0; i < maxvifs; i++) mc.mfcc_ttls[i] = oifs_ttl[i]; setsockopt(mrouter_s4, IPPROTO_IP, MRT_ADD_MFC, (void *)&mc, sizeof(mc));
/* IPv6 */ struct mf6cctl mc; memset(&mc, 0, sizeof(mc)); memcpy(&mc.mf6cc_origin, &source_addr, sizeof(mc.mf6cc_origin)); memcpy(&mc.mf6cc_mcastgrp, &group_addr, sizeof(mf6cc_mcastgrp)); mc.mf6cc_parent = iif_index; for (i = 0; i < maxvifs; i++) if (oifs_ttl[i] > 0) IF_SET(i, &mc.mf6cc_ifset); setsockopt(mrouter_s4, IPPROTO_IPV6, MRT6_ADD_MFC, (void *)&mc, sizeof(mc));
source_addr と group_addr は、(upcall メッセージで設定されるような) マルチキャストパケットのソースとグループアドレスです。 iif_index は、この特定のソースとグループアドレスのためのマルチキャストパケットが受信されるべきであるマルチキャストインタフェースの仮想インタフェースインデックスです。 oifs_ttl[] 配列は、マルチキャストパケットが発信インタフェースで転送されるために持つべきである最小の TTL (インタフェース毎の) を含んでいます。 TTL 値が 0 であるなら、対応するインタフェースは、発信インタフェースのセットに含まれていません。 IPv6 だけの場合に発信インタフェースのセットが指定できることに注意してください。
MFC エントリは、次によって削除されます:
/* IPv4 */ struct mfcctl mc; memset(&mc, 0, sizeof(mc)); memcpy(&mc.mfcc_origin, &source_addr, sizeof(mc.mfcc_origin)); memcpy(&mc.mfcc_mcastgrp, &group_addr, sizeof(mc.mfcc_mcastgrp)); setsockopt(mrouter_s4, IPPROTO_IP, MRT_DEL_MFC, (void *)&mc, sizeof(mc));
/* IPv6 */ struct mf6cctl mc; memset(&mc, 0, sizeof(mc)); memcpy(&mc.mf6cc_origin, &source_addr, sizeof(mc.mf6cc_origin)); memcpy(&mc.mf6cc_mcastgrp, &group_addr, sizeof(mf6cc_mcastgrp)); setsockopt(mrouter_s4, IPPROTO_IPV6, MRT6_DEL_MFC, (void *)&mc, sizeof(mc));
次の方法は、カーネルにインストールされた MFC エントリ毎に様々な統計値 (例えば、ソースとグループアドレス毎の転送パケットの数) を得るために使用できます:
/* IPv4 */ struct sioc_sg_req sgreq; memset(&sgreq, 0, sizeof(sgreq)); memcpy(&sgreq.src, &source_addr, sizeof(sgreq.src)); memcpy(&sgreq.grp, &group_addr, sizeof(sgreq.grp)); ioctl(mrouter_s4, SIOCGETSGCNT, &sgreq);
/* IPv6 */ struct sioc_sg_req6 sgreq; memset(&sgreq, 0, sizeof(sgreq)); memcpy(&sgreq.src, &source_addr, sizeof(sgreq.src)); memcpy(&sgreq.grp, &group_addr, sizeof(sgreq.grp)); ioctl(mrouter_s6, SIOCGETSGCNT_IN6, &sgreq);
次の方法は、カーネルでマルチキャスト仮想インタフェース毎の様々な統計値 (例えば、インタフェース毎の転送パケットの数) を得るために使用できます:
/* IPv4 */ struct sioc_vif_req vreq; memset(&vreq, 0, sizeof(vreq)); vreq.vifi = vif_index; ioctl(mrouter_s4, SIOCGETVIFCNT, &vreq);
/* IPv6 */ struct sioc_mif_req6 mreq; memset(&mreq, 0, sizeof(mreq)); mreq.mifi = vif_index; ioctl(mrouter_s6, SIOCGETMIFCNT_IN6, &mreq);
高度なマルチキャスト API プログラミングガイド
カーネルに新しい機能を追加したいと思うなら、後方互換性 (バイナリと API) を保つのが難しくなって、同時にユーザレベルプロセスに (カーネルがそれらをサポートするなら) 新しい機能を利用することを許容します。私たちが後方互換性を保つことができるようするメカニズムの 1 つは、ユーザレベルプロセスとカーネルの間の一種のネゴシエーション (交渉) です:
- ユーザレベルプロセスは、カーネルで使用したい新しい機能 (と対応する API) のセットを有効にしようとします。
- カーネルは、それについて知っている機能の (サブ) セットを返して、進んで有効にします。
- ユーザレベルプロセスは、カーネルが同意した機能のセットだけを使用します。
後方互換性をサポートするために、ユーザレベルプロセスが少しも新しい機能を要求しないなら、カーネルは、基本的なマルチキャスト API ( プログラミングガイド セクションを参照) をデフォルトとします現在は、高度なマルチキャスト API は、IPv4 のためだけに存在します。将来は、同様に IPv6 サポートがあるでしょう。
下記は、拡張可能な API ソリューション (解決法) の概要です。すべての新しいオプションと構造体は、ほかに規定されたものがある場合を除き < netinet/ip_mroute.h> と < netinet6/ip6_mroute.h> で定義されることに注意してください。
ユーザレベルプロセスは、カーネルとの API 機能ネゴシエーション (交渉) を実行するために、新しい getsockopt()/ setsockopt() オプションを使用します。このネゴシエーション (交渉) は、マルチキャストルーティングソケットがオープンされた直後に実行しなければなりません。要求/許容の機能のセット (集合) は、ビットセットとして保存されます (現在、 uint32_t で、すなわち、最大 32 の新しい機能です)。新しい getsockopt()/ setsockopt() オプションは、 MRT_API_SUPPORT と MRT_API_CONFIG です。例えば:
uint32_t v; getsockopt(sock, IPPROTO_IP, MRT_API_SUPPORT, (void *)&v, sizeof(v));
これは、 v にカーネル API サポートで前もって定義されたビットを設定します。 uint32_t 中の 8 つの最下位ビットは、他の新しい機能について 24 個のフラグを残す struct mfcctl (これらのフラグについては、下記参照) の新しい定義の一部として mfcc_flags で使用することができる、可能性がある 8 つのフラグ MRT_MFC_FLAGS_* と同様です。 getsockopt( MRT_API_SUPPORT) によって返された値は、読み込み専用です。言い換えれば、 setsockopt( MRT_API_SUPPORT) 失敗するでしょう。
API を変更して、カーネルに何らかの特有の機能を設定するためには:
uint32_t v = MRT_MFC_FLAGS_DISABLE_WRONGVIF; if (setsockopt(sock, IPPROTO_IP, MRT_API_CONFIG, (void *)&v, sizeof(v)) != 0) { return (ERROR); } if (v & MRT_MFC_FLAGS_DISABLE_WRONGVIF) return (OK); /* 成功 */ else return (ERROR);
言い換えれば、 setsockopt( MRT_API_CONFIG) が呼び出されるとき、それへの引数は、API とカーネルで有効にされる機能の要求されるセットを指定します。 v のリターン値は、カーネルで有効にされた機能の実際の (サブ) セットです。有効にされた機能の同じセットを後で取得ためには:
getsockopt(sock, IPPROTO_IP, MRT_API_CONFIG, (void *)&v, sizeof(v));
有効にされた機能のセットは、グローバルです。言い換えれば、 setsockopt( MRT_API_CONFIG) は、 setsockopt( MRT_INIT) の直後に呼び出されるべきです。
現在は、新しい機能の次のセットは、次のように定義されます:
#define MRT_MFC_FLAGS_DISABLE_WRONGVIF (1 << 0) /* WRONGVIF シグナルを 無効にする */ #define MRT_MFC_FLAGS_BORDER_VIF (1 << 1) /* ボーダ vif */ #define MRT_MFC_RP (1 << 8) /* RP アドレスを有効にする */ #define MRT_MFC_BW_UPCALL (1 << 9) /* bw upcall を有効にする */
高度なマルチキャスト API は、旧来の struct mfcctl2 の代わりに新たに定義された struct mfcctl を使用します。オリジナルの struct mfcctl は、そのままに保持されます。新しい struct mfcctl2 は、以下の通りです:
/* * MRT_ADD_MFC と MRT_DEL_MFC のための新しい引数の構造体は、 * 古い struct mfcctl をオーバレイして拡張しています。 */ struct mfcctl2 { /* the mfcctl fields */ struct in_addr mfcc_origin; /* マルチキャストの ip 始点 */ struct in_addr mfcc_mcastgrp; /* マルチキャストグループ関連*/ vifi_t mfcc_parent; /* 着信 vif */ u_char mfcc_ttls[MAXVIFS];/* vif の転送 ttl */ /* extension fields */ uint8_t mfcc_flags[MAXVIFS];/* MRT_MFC_FLAGS_* フラグ */ struct in_addr mfcc_rp; /* RP アドレス */ };
新しいフィールドは、 mfcc_flags[MAXVIFS] と mfcc_rp です。互換性の理由のためにそれらが終わりに追加されることに注意してください。
mfcc_flags[MAXVIFS] フィールドは、(S,G) エントリ単位でインタフェース毎の様々なフラグを設定するために使用されます。現在、定義されたフラグは、次の通りです:
#define MRT_MFC_FLAGS_DISABLE_WRONGVIF (1 << 0) /* WRONGVIF シグナルを 無効にする */ #define MRT_MFC_FLAGS_BORDER_VIF (1 << 1) /* ボーダ vif */
MRT_MFC_FLAGS_DISABLE_WRONGVIF フラグは、マルチキャストデータパケットが間違ったインタフェースで到着するなら、(S,G) 精度で IGMPMSG_WRONGVIF カーネルシグナルを明らかに無効にするために使用されます。通常、このシグナルは、 PIM-SM マルチキャストルーティングの場合に最短パスのスイッチを完了するか、または PIM アサートメッセージをトリガするために使用されます。しかしながら、発信インタフェースセットでなくて、着信インタフェースになると期待していないインタフェースに配信するべきではありません。したがって、 MRT_MFC_FLAGS_DISABLE_WRONGVIF フラグがインタフェースのいくつかに設定されるなら、データパケットは、MFC エントリが WRONGVIF シグナルをトリガしないインタフェースで到着します。そのフラグが設定されないなら、シグナルは、トリガされます (デフォルト動作)。
MRT_MFC_FLAGS_BORDER_VIF フラグは、PIM レジスタメッセージの境界ビットが設定されるべきであるかどうか (レジスタカプセル化がカーネルの中で実行されるときの場合に) 指定するために使用されます。それが特別な PIM レジスタカーネル仮想インタフェースに設定されるなら ( pim(4) 参照)、RP に送られたレジスタメッセージ中の境界ビットが設定されます。
残っている 6 ビットは、将来の使用のために予約されています。
mfcc_rp フィールドは、カーネルレベル PIM レジスタカプセル化を実行したいと思うなら、マルチキャストグループ G に (PIM-SM マルチキャストルーティングの場合に) RP アドレスを指定するためにに使用されます。 mfcc_rp フィールドは、 MRT_MFC_RP 高度な API フラグ/ケーパビリティが setsockopt( MRT_API_CONFIG) によってうまく設定された場合にだけ、使用されます。
MRT_MFC_RP フラグが setsockopt( MRT_API_CONFIG) によってうまく設定されたなら、カーネルは、ユーザレベルカプセル化のためにマルチキャストデータパケットをユーザレベル ( IGMPMSG_WHOLEPKT upcall の中の) に送る代わりに PIM レジスタカプセル化自体を実行するのを試みます。 RP アドレスは、新しい struct mfcctl2 内の mfcc_rp フィールドから取られるでしょう。しかしながら、 MRT_MFC_RP フラグがうまく設定されたとしても、 mfcc_rp フィールドが INADDR_ANY に設定されたなら、カーネルは、まだマルチキャストデータパケットと共に IGMPMSG_WHOLEPKT upcall をユーザレベルプロセスに配信します。
さらに、マルチキャストデータパケットが PIM レジスタカプセル化の後で単一の IP パケットの中で適合することができないくらい大きいなら (例えば、約 65500 バイトのサイズがあったなら)、データパケットは、断片化され、それぞれの断片は、別々にカプセル化されます。通常、カプセル化を実行するのと同じホストから局所的に送出された場合にだけ、マルチキャストデータパケットが大きい場合があることに注意してください。そうでなければ、例えばイーサネット上のマルチキャストデータパケットの送信は、もっと小さな部分に断片化したでしょう。
通常、マルチキャストルーティングのユーザレベルプロセスは、何らかのデータフローについて転送帯域幅を知る必要があるでしょう。例えば、マルチキャストルーティングプロセスがタイムアウトアイドル MFC エントリを必要とするかもしれません、または例えば、帯域幅レートがスレッシュホールドを超えているなら、 PIM-SM の場合に (S,G) 最短パススイッチを開始することができます。
データフローの帯域幅を測定する独創的な解決策は、ユーザレベルプロセスが定期的に (S,G) あたりの転送パケット/バイトの数に関してカーネルに問い合わせることでした、そして、そしてそれらの数に基づいて、ソース (始点) がアイドルであるかどうか、またはソースの通信帯域幅がスレッシュホールドを超えているかどうかを評価するでしょう。その解決策は、決してスケーラブル (測定可能) ではありません、したがって、帯域幅モニタリングのための新しいメカニズムが必要です。
次は、帯域幅のモニタリングのメカニズムの説明です。
- データフローの帯域幅がいくつかの前もって定義されたフィルタを満足させるなら、カーネルは、マルチキャストルーティングソケット上の upcall をそのフィルタをインストールしたマルチキャストルーティングプロセスに配信します。
- 帯域幅 upcall フィルタは、(S,G) 単位でインストールされます。 (S,G) ごとに 1 つ以上のフィルタがあり得ます。
- すべての可能な比較操作 (すなわち、< <= == != > >= ) をサポートする代わりに、カーネルレベルの実装をより簡単にして、実質的にそれらの 2 だけを必要とするので、 <= と >= 操作だけサポートがあります。さらに、不足している操作は、それらの <= と >= フィルタの二次ユーザレベルフィルタリングによってシミュレートすることができます。例えば、!= をシミュレートするために、フィルタ“bw <= 0xffffffff”をインストールする必要があり、そして、upcall が受け付けられた後に“measured_bw != expected_bw”であるかどうかチェックする必要があります。
- 帯域幅 upcall メカニズムは、 MRT_MFC_BW_UPCALL フラグのために setsockopt( MRT_API_CONFIG) によって有効にされます。
- 帯域幅 upcall フィルタは、それぞれ新しい setsockopt( MRT_ADD_BW_UPCALL) と setsockopt( MRT_DEL_BW_UPCALL) (もちろん、適切な struct bw_upcall 引数で) によって追加されるか/削除されます。
アプリケーション観点から、開発者は、次のことについて知る必要があります:
/* * 測定される帯域幅がスレッシュホールドを越えているかまたは下回るなら、 * upcall をインストールするか、または配信するるための構造体。 * * ユーザプログラム (例えば、デーモン) は、いくつかのデータフローに * よって使用される帯域幅がいつ、スレッシュホールドが越えているか * または下回るかを知る必要があるかもしれません。このインタフェースは、 * ユーザランドが (バイト単位、およびパケット単位で) スレッシュホールド * と測定される間隔を指定できるようにします。フローは、同じ発信元 (始点) * と宛先 (終点) IP アドレスがあるすべてのパケットです。 * 現在のところ、コードは、マルチキャストの宛先 (終点) に使用される * だけですが、ユニキャストの使用を防ぐものはありません。 * * 測定される間隔は、いくらかの Tmin (現在 3 秒) より短いはずがありません。 * スレッシュホールドは、per_interval のパケット単位およびバイト単位で * 設定されます。 * * 測定は、次の通り動作します: * * >= 測定値のために: * 最初のパケットは、測定される間隔の始まりを示します。間隔の間、私たちは、 * パケットとバイトを数えます、そして、スレッシュホールドが渡されたとき、 * 私たちは、upcall を配信し、終わります。間隔の終わりの後の最初のパケット * は、カウントをリセットして、測定を再開します。 * * <= 測定値のために: * 私たちは、間隔の終わりで発火するようにタイマを始動し、それぞれの着信 * パケットに関して、パケットとバイトを数えます。タイマが発火するとき、 * 私たちは、スレッシュホールドと値を比べて、下回っているなら upcall * をスケジュールし、(タイマを再スケジュールしカウンタを 0 にして) * 測定を再開します。 */ struct bw_data { struct timeval b_time; uint64_t b_packets; uint64_t b_bytes; }; struct bw_upcall { struct in_addr bu_src; /* ソース (始点) アドレス */ struct in_addr bu_dst; /* 宛先 (終点) アドレス */ uint32_t bu_flags; /* その他のフラグ (下記参照) */ #define BW_UPCALL_UNIT_PACKETS (1 << 0) /* (パケット単位の) スレッシュホールド */ #define BW_UPCALL_UNIT_BYTES (1 << 1) /* (バイト単位の) スレッシュホールド */ #define BW_UPCALL_GEQ (1 << 2) /* bw >= スレッシュホールド なら, upcall */ #define BW_UPCALL_LEQ (1 << 3) /* bw <= スレッシュホールド なら, upcall */ #define BW_UPCALL_DELETE_ALL (1 << 4) /* s,d のための upcall をすてべ削除 */ struct bw_data bu_threshold; /* bw スレッシュホールド */ struct bw_data bu_measured; /* 測定される bw */ }; /* 一緒に配送する upcall の最大数 */ #define BW_UPCALLS_MAX 128 /* 帯域幅測定のための最小スレッシュホールド時間の間隔 */ #define BW_UPCALL_THRESHOLD_INTERVAL_MIN_SEC 3 #define BW_UPCALL_THRESHOLD_INTERVAL_MIN_USEC 0
bw_upcall 構造体は、 setsockopt( MRT_ADD_BW_UPCALL) と setsockopt( MRT_DEL_BW_UPCALL) への引数として使用されます。各 setsockopt( MRT_ADD_BW_UPCALL) は、 bw_upcall 引数で送信元 (始点) と宛先 (終点) アドレスのためにカーネルにフィルタをインストールし、そして、そのフィルタは、次の疑似アルゴリズムに従って upcall をトリガします:
if (bw_upcall_oper IS ">=") { if (((bw_upcall_unit & PACKETS == PACKETS) && (measured_packets >= threshold_packets)) || ((bw_upcall_unit & BYTES == BYTES) && (measured_bytes >= threshold_bytes))) SEND_UPCALL("measured bandwidth is >= threshold"); } if (bw_upcall_oper IS "<=" && measured_interval >= threshold_interval) { if (((bw_upcall_unit & PACKETS == PACKETS) && (measured_packets <= threshold_packets)) || ((bw_upcall_unit & BYTES == BYTES) && (measured_bytes <= threshold_bytes))) SEND_UPCALL("measured bandwidth is <= threshold"); }
同じ bw_upcall では、BYTE と PACKET の両方でユニットを指定することができます。しかしながら、GEQ と LEQ フラグは、互いに排他的です。
基本的に、upcall は、測定される帯域幅が (指定された測定間隔中に) スレッシュホールド帯域幅より >= か <= なら、配信されます。実際的な理由で、測定する間隔の最も小さい値は、3 秒です。より小さい値が許可されるなら、帯域幅見積りがそれほど正確でないし、潜在的に生成された upcall の大変高い頻度は、あまりに多くのオーバヘッドを持ち込むかもしれません。 >= 操作のために、答えは、 threshold_interval の終わりまでに知っているかもしれません、したがって、 upcall は、より早く配信されるかもしれません。しかしながら、<= 操作のために、スレッシュホールドの間隔が答えを知るための期限が切れるまで、私たちは、待たなければなりません。
使用例:
struct bw_upcall bw_upcall; /* すべての bw_upcall フィールドを適切に割り当てます */ memset(&bw_upcall, 0, sizeof(bw_upcall)); memcpy(&bw_upcall.bu_src, &source, sizeof(bw_upcall.bu_src)); memcpy(&bw_upcall.bu_dst, &group, sizeof(bw_upcall.bu_dst)); bw_upcall.bu_threshold.b_data = threshold_interval; bw_upcall.bu_threshold.b_packets = threshold_packets; bw_upcall.bu_threshold.b_bytes = threshold_bytes; if (is_threshold_in_packets) bw_upcall.bu_flags |= BW_UPCALL_UNIT_PACKETS; if (is_threshold_in_bytes) bw_upcall.bu_flags |= BW_UPCALL_UNIT_BYTES; do { if (is_geq_upcall) { bw_upcall.bu_flags |= BW_UPCALL_GEQ; break; } if (is_leq_upcall) { bw_upcall.bu_flags |= BW_UPCALL_LEQ; break; } return (ERROR); } while (0); setsockopt(mrouter_s4, IPPROTO_IP, MRT_ADD_BW_UPCALL, (void *)&bw_upcall, sizeof(bw_upcall));
単一のフィルタを削除するために、 MRT_DEL_BW_UPCALL を使用し、 bw_upcall のフィールドは、 MRT_ADD_BW_UPCALL が呼び出された時と全く同じに設定しなければなりません。
与えられた (S,G) のためのすべての帯域幅を削除するために、 struct bw_upcall 中の bu_src と bu_dst フィールドのみを設定する必要があり、つぎにフィールド bw_upcall.bu_flags 内の BW_UPCALL_DELETE_ALL フラグだけを設定します。
帯域幅 upcall は、新しい upcall メッセージでそれらを集めることによって受け付けます:
#define IGMPMSG_BW_UPCALL 4 /* BW モニタリング upcall */
このメッセージは、 struct bw_upcall ( BW_UPCALLS_MAX = 128 までの) 要素の配列です。 128 保留中 (pending) の upcall があるとき、または前の upcall 以来 1 秒の期限が切れているとき (どれが最初になっても)、 upcall は、配信されます。 struct upcall 要素では、 bu_measured フィールドは、特定の測定値を示すために書き込まれます。しかしながら、特定の間隔が測定される方法のために、ユーザは、 bu_measured.b_time がどのように使用されているか注意するべきです。例えば、フィルタが upcall をトリガするためにインストールされ、パケットの数が >= 1 であるなら、 bu_measured は、>= フィルタのための測定される間隔が転送されたパケットによって“時間を計る”ので、最初のものの後の upcall で 0 の値であるかもしれません。したがって、転送されたデータの帯域幅の正確な値を測定するのにこの upcall メカニズムを使用するべきではありません。正確な帯域幅を測定するために、ユーザは、 ioctl( SIOCGETSGCNT) メカニズム ( プログラミングガイド セクションを参照) で転送されたパケット統計値を得る必要があるでしょう。
フィルタのための upcall は、特定のフィルタが削除されるまで、配信されますが、 bu_threshold.b_time 毎に一度より頻繁ではないことに注意してください。例えば、フィルタがシグナルを配信するために指定され、 bw >= 1 パケットであるなら、最初のパケットは、シグナルのトリガとなりますが、次の upcall は、前の upcall の後に bu_threshold.b_time より早くトリガされません。
関連項目
altq(4), dummynet(4), getsockopt(2), gif(4), gre(4), recvfrom(2), recvmsg(2), setsockopt(2), socket(2), sourcefilter(3), icmp6(4), igmp(4), inet(4), inet6(4), intro(4), ip(4), ip6(4), mld(4), pim(4)歴史
Distance Vector Multicast Routing Protocol (DVMRP) は、最初に開発されたマルチキャストルーティングプロトコルでした。その後に、Multicast Extensions to OSPF (MOSPF) のような他のプロトコルと Core Based Trees (CBT) が同様に開発されました。現在、自律システム境界のルータは、 Border Gateway Protocol (BGP) を通してピア (相手側) とマルチキャストルートを交換できます。他の多くのルーティングプロトコルは、 PIM-SM と PIM-DM で使用するためにマルチキャストルートを再配信することができます。作者
元のマルチキャストコードは、 (BBN Labs) によって書かれ、後で次の個人によって修正されました: (Stanford), (Stanford), (LBL), (PARC), (PARC)。 IPv6 マルチキャストサポートは、KAME プロジェクト ( http://www.kame.net) によって実装され、IPv4 マルチキャストコードに基づいています。高度なマルチキャスト API とマルチキャスト帯域幅モニタリングは、 (NextHop) と共同して (ICSI) によって実装されました。このマニュアルページは、
(ICSI) によって書かれました。May 27, 2009 | FreeBSD |