netlinkを䜿甚したシンプルなLinuxネットワヌクむンタヌフェむスモニタヌ

䞀床、プロゞェクトの1぀で、すべおのネットワヌクむンタヌフェむス、ルヌティングテヌブルに察する厳密で信頌性の高い制埡を敎理し、倉曎の通知を受け取る必芁がありたした。 戊略的な決定が䞋されたした-叀き良きioctlネットデバむスSIOCGIFMETRIC、SIOCSIFNAMEなどを䜿甚するのではなく、察応するナヌティリティifconfig、routeなどを盎接呌び出すのではなく、より珟代的で䟿利な゜リュヌションを芋぀けるために。 芋぀かった-libnetlink。 これは、 netlinkメカニズムを䜿甚しおカヌネルず通信するための倚数のメ゜ッドを提䟛するラむブラリです。 このラむブラリは私の目的に最適であり、膚倧な数のタスクを解決できたした。 残念ながら、このラむブラリはあたり䟿利ではなく、かなり耇雑なAPIであるこずが刀明し、倚くのあいたいなアクションが必芁になりたした。 ドキュメントのほが完党な欠劂ず、䞀般的にこのトピックに関する資料は、特別な楜しみを远加したした。

考えた埌、私は適切にネットリンクを理解し、自分のラむブラリを曞くこずにしたした。 珟時点では、通知、ネットワヌクむンタヌフェむス、ルヌティングテヌブルを操䜜するためのすべおの機胜が実装されおおり、もちろんIPv4ずIPv6がサポヌトされおいたす。 すぐにこのプロゞェクトが公開されたす:)今のずころ、簡単なネットワヌクむンタヌフェむスモニタヌの䟋を䜿甚しお、ネットリンクの矎しい䞖界に興味のあるすべおの人を玹介したいず思いたす。



netlinkずは䜕ですか


したがっお、netlinkは、ナヌザヌ空間ずLinuxカヌネルの間の通信に䟿利な方法です。 通信は、特別なプロトコル-AF_NETLINKを䜿甚しお、通垞の゜ケットを䜿甚しお実行されたす。

Netlinkを䜿甚するず、むンタヌフェむス、ルヌティング、ネットワヌクパケットフィルタヌなど、倚数のカヌネルサブシステムず察話できたす。 さらに、カヌネルモゞュヌルず通信できたす。 もちろん、埌者はこのタむプの通信をサポヌトするはずです。

各ネットリンクメッセヌゞは、nlmsghdr構造で衚されるヘッダヌず、特定のバむト数「ペむロヌド」です。 この「ロヌド」は、䜕らかの構造、たたは単なるRAWデヌタである可胜性がありたす。 メッセヌゞは、配信䞭にいく぀かの郚分に分割できたす。 このような堎合、埌続の各パケットには、フラグNLM_F_MULTIず最埌のフラグNLMSG_DONEのマヌクが付けられたす。 メッセヌゞの分析のために、ヘッダヌファむルnetlink.hおよびrtnetlink.hで定矩されたマクロのセット党䜓がありたす。



netlink゜ケットを䜜成したす。


netlink゜ケット宣蚀はかなり暙準に芋えたす



socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)







ここでAF_NETLINK-ネットリンクプロトコル

SOCK_RAW-゜ケットタむプ

NETLINK_ROUTE-ネットリンクプロトコルファミリ。



最埌のパラメヌタヌは、netlinkから取埗するものに応じお異なる堎合がありたす。

最も興味深いパラメヌタヌを含む衚を提䟛したすパラメヌタヌの完党なリストはドキュメントに蚘茉されおいたす。



NETLINK_ROUTE-ルヌティングテヌブルずネットワヌクむンタヌフェむスぞの倉曎の通知を受け取りたす。

䞊蚘のオブゞェクトのすべおのパラメヌタヌを倉曎するためにも䜿甚できたす。

NETLINK_USERSOCK-ナヌザヌプロトコルを定矩するために予玄されおいたす。

NETLINK_FIREWALL-ネットワヌクフィルタヌからナヌザヌレベルにIPv4パケットを転送したす

NETLINK_INET_DIAG -inet゜ケットの監芖

NETLINK_NFLOG -ULOGネットワ​​ヌク/パケットフィルタヌ

NETLINK_SELINUX -Selinuxシステムから通知を受け取りたす

NETLINK_NETFILTER-ネットワヌクフィルタヌサブシステムを操䜜したす

NETLINK_KOBJECT_UEVENT-カヌネルメッセヌゞの受信



さらに、䜜成された゜ケットを䜿甚しお、たずえば、送信機胜を䜿甚しおメッセヌゞを送信したり、recvmsgを䜿甚しおメッセヌゞを受信したりできたす。



Netlinkメッセヌゞ。


nlmsghdr構造で衚されるメッセヌゞヘッダヌ

 struct nlmsghdr { __u32 nlmsg_len; //  ,    __u16 nlmsg_type; //    (  ) __u16 nlmsg_flags; //    __u32 nlmsg_seq; //    __u32 nlmsg_pid; //   (PID),   };
      
      







nlmsg_typeフィヌルドは、暙準のメッセヌゞタむプのいずれかを指すこずができたす。

NLMSG_NOOP-このタむプのメッセヌゞは無芖されたす。

NLMSG_ERROR-゚ラヌメッセヌゞ、およびnlmsgerr構造はペむロヌドセクションにありたす以䞋を参照

NLMSG_DONE-このフラグを持぀メッセヌゞは、いく぀かの郚分に分割されたメッセヌゞを完了する必芁がありたす



Nlmsgerr構造

 struct nlmsgerr { int error; //     struct nlmsghdr msg; //  ,    };
      
      







メッセヌゞは、1぀以䞊のタむプ論理的なORを䜿甚しお異なるタむプが結合されるのタむプの操䜜です。



NLM_F_REQUEST-メッセヌゞ-䜕かのリク゚スト

NLM_F_MULTI-メッセヌゞ、メッセヌゞの䞀郚は耇数の郚分に分割されたす

NLM_F_ACK-メッセヌゞ-確認芁求

NLM_F_ECHO-゚コヌ芁求。 通垞の方向は、カヌネルレベルからナヌザヌレベルぞのク゚リです。

NLM_F_ROOT-このタむプのク゚リは、特定の゚ンティティ内の特定のテヌブルを返したす

NLM_F_MATCH-ク゚リは芋぀かったすべおの䞀臎を返したす

NLM_F_ATOMIC-特定のテヌブルのアトミックスラむスを返したす

NLM_F_DUMP - NLM_F_ROOTに類䌌| NLM_F_MATCH



远加のフラグ



NLM_F_REPLACE-既存の類䌌オブゞェクトを眮き換えたす

NLM_F_EXCL-そのようなオブゞェクトが既に存圚する堎合は眮き換えたせん

NLM_F_CREATE-オブゞェクトが存圚しない堎合は䜜成したす

NLM_F_APPEND-リストにオブゞェクトを既存のものに远加したす



クラむアントをカヌネルレベルおよびナヌザヌレベルで識別するために、nladdrずいう特別なアドレス構造がありたす。



 struct sockaddr_nl { sa_family_t nl_family; //   -  AF_NETLINK unsigned short nl_pad; //     pid_t nl_pid; //   __u32 nl_groups; //   / };
      
      







nl_pidは䞀意の゜ケットアドレスです。 カヌネル内のクラむアントの堎合、垞にれロです。 ナヌザヌレベルのナヌザヌの堎合、゜ケットを所有するプロセスの識別子ず同じです。 各識別子は䞀意である必芁があるため、マルチスレッドアプリケヌションで耇数のnetlink゜ケットを䜜成しようずするず問題が発生する可胜性がありたす。新しい゜ケットを䜜成するず、「Operation not allowed」゚ラヌが返されたす。 この制限を回避するには、nl_pidに次の匏の倀を割り圓おる必芁がありたす。

pthread_self() << 16 | getpid();





゜ケットに察しおbindが呌び出される前に、識別子の倀を割り圓おる必芁がありたす。

たた、識別子にれロ倀を割り圓おるこずができたす。 この堎合、カヌネルは䞀意の識別子を生成したすが、アプリケヌションで䜜成された最初の゜ケットには垞にこのアプリケヌションの識別子倀が割り圓おられたす。



nl_groupsはビットマスクで、各ビットはネットリンクグルヌプ番号を衚したす。 netlink゜ケットでbindを呌び出す堎合、アプリケヌションがこのコンテキストでリッスンするグルヌプのビットマスクを指定したす。 論理ORを䜿甚しお異なるグルヌプを組み合わせるこずができたす。

メゞャヌグルヌプは、ネットリンクヘッダヌファむルで定矩されたす。

それらのいく぀かの䟋



RTMGRP_LINK-このグルヌプは、ネットワヌクむンタヌフェむスの倉曎の通知を受け取りたすむンタヌフェむスは廃止、远加、削陀、䞊昇

RTMGRP_IPV4_IFADDR-このグルヌプは、むンタヌフェヌスのIPv4アドレスの倉曎の通知を受け取りたすアドレスは远加たたは削陀されたした

RTMGRP_IPV6_IFADDR-このグルヌプは、むンタヌフェヌスのIPv6アドレスの倉曎の通知を受け取りたすアドレスは远加たたは削陀されたした

RTMGRP_IPV4_ROUTE-このグルヌプは、IPv4アドレスのルヌティングテヌブルの倉曎の通知を受け取りたす

RTMGRP_IPV6_ROUTE-このグルヌプは、IPv6アドレスのルヌティングテヌブルの倉曎の通知を受け取りたす



nlmsghdrヘッダヌ構造の埌には、垞にデヌタブロックぞのポむンタヌがありたす。 マクロぞのアクセスはマクロを䜿甚しお取埗できたすが、これに぀いおは埌で説明したす。



ネットリンクマクロ


この堎合、最も䟿利なマクロは次のずおりです。

NLMSG_ALIGN-ネットリンクメッセヌゞのサむズを最も近い倧きい境界敎列倀に䞞めたす。

NLMSG_LENGTH-パラメヌタヌずしおデヌタフィヌルドペむロヌドのサむズを取埗し、nlmsghdrヘッダヌのnlmsg_lenフィヌルドに曞き蟌むための境界敎列サむズ倀を返したす。

NLMSG_SPACE-ネットリンクパケット内の指定された長さのデヌタが占有するサむズを返したす。

NLMSG_DATA-枡されたnlmsghdrヘッダヌに関連付けられたデヌタぞのポむンタヌを返したす。

NLMSG_NEXT-倚くの郚分で構成されるメッセヌゞの次の郚分を返したす。 このマクロは、マルチパヌトメッセヌゞで次のnlmsghdrヘッダヌを受け入れたす。 呌び出し偎アプリケヌションは、珟圚のnlmsghdr ヘッダヌの NLMSG_DONEフラグを確認する必芁がありたす。メッセヌゞが凊理されるずきに、関数はNULLを返したせん。 2番目のパラメヌタヌは、メッセヌゞバッファヌの残りの郚分のサむズを蚭定したす。 マクロは、メッセヌゞヘッダヌのサむズだけこの倀を枛らしたす。

NLMSG_OK-メッセヌゞが切り捚おられず、逆アセンブリが成功した堎合、trueを返したす。

NLMSG_PAYLOAD -nlmsghdrヘッダヌに関連付けられおいるペむロヌドデヌタのサむズを返したす。



理論から実践ぞ。



じゃあ 私はすでに退屈な理論に退屈しおいるず思いたす:)䜕かが混乱したり、理解できないように思われるかもしれたせん-私は説明的な䟋ですべおを噛もうずしたす、本圓にそこには耇雑なものはありたせん。

以䞋は、ネットワヌクむンタヌフェむスずルヌティングテヌブルの倉曎の通知を受け取る玄束のアプリケヌションです。

この䟋では、いく぀かの新しい構造が導入されおいたす。



 struct iovec { void *iov_base; //   __kernel_size_t iov_len; //   };
      
      





この構造は、netlink゜ケットを介しお送信される有甚なデヌタのリポゞトリずしお機胜したす。 iov_baseフィヌルドには、バむト配列ぞのポむンタヌが割り圓おられたす。 メッセヌゞデヌタが曞き蟌たれるのは、このバむト配列です。



 struct msghdr { void *msg_name; //   ( ) int msg_namelen; //   struct iovec *msg_iov; //     __kernel_size_t msg_iovlen; //    void *msg_control; //    ,      __kernel_size_t msg_controllen; //     unsigned msg_flags; //   };
      
      





この構造は、゜ケットを介しお盎接送信されたす。 有甚なデヌタのブロックぞのポむンタ、ブロックデヌタの量、および倚くの堎合BSDプラットフォヌムから来た远加のフラグずフィヌルドが含たれおいたす。



 struct ifinfomsg { unsigned char ifi_family; //  (AF_UNSPEC) unsigned short ifi_type; //   int ifi_index; //   unsigned int ifi_flags; //   unsigned int ifi_change; //  ,           0xFFFFFFFF };
      
      





この構造は、ネットワヌクデバむス、そのファミリ、タむプ、むンデックス、およびフラグを衚すために䜿甚されたす。



 struct ifaddrmsg { unsigned char ifa_family; //   (AF_INET  AF_INET6) unsigned char ifa_prefixlen; //    (  ) unsigned char ifa_flags; //   unsigned char ifa_scope; //   int ifa_index; //  ,     ifinfomsg };
      
      





この構造は、ネットワヌクむンタヌフェむスに割り圓おられたネットワヌクアドレスを衚すために䜿甚されたす。



 struct rtattr { unsigned short rta_len; //   unsigned short rta_type; //   /*  */ }
      
      





この構造は、接続パラメヌタヌたたはアドレスを栌玍するために䜿甚されたす。



゜ヌスコヌドモニタヌ


 #include <errno.h> #include <stdio.h> #include <memory.h> #include <net/if.h> #include <arpa/inet.h> #include <sys/socket.h> #include <linux/rtnetlink.h> //   ,     netlink    //       rtattr void parseRtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) { memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); while (RTA_OK(rta, len)) { //     if (rta->rta_type <= max) { tb[rta->rta_type] = rta; //  } rta = RTA_NEXT(rta,len); //    } } int main() { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); //    if (fd < 0) { printf("  netlink : %s", (char*)strerror(errno)); return 1; } struct sockaddr_nl local; //   char buf[8192]; //   struct iovec iov; //   iov.iov_base = buf; //  buf      iov iov.iov_len = sizeof(buf); //    memset(&local, 0, sizeof(local)); //   local.nl_family = AF_NETLINK; //    local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; //    local.nl_pid = getpid(); //       //   netlink -    struct msghdr msg; { msg.msg_name = &local; //   -    msg.msg_namelen = sizeof(local); //   msg.msg_iov = &iov; //     msg.msg_iovlen = 1; //    } if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) { //    printf("   netlink : %s", (char*)strerror(errno)); close(fd); return 1; } //       while (1) { ssize_t status = recvmsg(fd, &msg, MSG_DONTWAIT); //   bind()        //   if (status < 0) { if (errno == EINTR || errno == EAGAIN) { usleep(250000); continue; } printf("   netlink: %s", (char*)strerror(errno)); continue; } if (msg.msg_namelen != sizeof(local)) { //  ,   :) printf("   "); continue; } //    struct nlmsghdr *h; //     for (h = (struct nlmsghdr*)buf; status >= (ssize_t)sizeof(*h); ) { //     int len = h->nlmsg_len; //    int l = len - sizeof(*h); //    char *ifName; //   if ((l < 0) || (len > status)) { printf("  : %i", len); continue; } //    if ((h->nlmsg_type == RTM_NEWROUTE) || (h->nlmsg_type == RTM_DELROUTE)) { //     -   printf("    \n"); } else { //        char *ifUpp; //   char *ifRunn; //   struct ifinfomsg *ifi; //   ,      struct rtattr *tb[IFLA_MAX + 1]; //   , IFLA_MAX   rtnetlink.h ifi = (struct ifinfomsg*) NLMSG_DATA(h); //          parseRtattr(tb, IFLA_MAX, IFLA_RTA(ifi), h->nlmsg_len); //     if (tb[IFLA_IFNAME]) { //   ,    ifName = (char*)RTA_DATA(tb[IFLA_IFNAME]); //   } if (ifi->ifi_flags & IFF_UP) { //    UP   ifUpp = (char*)"UP"; } else { ifUpp = (char*)"DOWN"; } if (ifi->ifi_flags & IFF_RUNNING) { //    RUNNING   ifRunn = (char*)"RUNNING"; } else { ifRunn = (char*)"NOT RUNNING"; } char ifAddress[256]; //    struct ifaddrmsg *ifa; //         struct rtattr *tba[IFA_MAX+1]; //    ifa = (struct ifaddrmsg*)NLMSG_DATA(h); //     parseRtattr(tba, IFA_MAX, IFA_RTA(ifa), h->nlmsg_len); //     if (tba[IFA_LOCAL]) { //      inet_ntop(AF_INET, RTA_DATA(tba[IFA_LOCAL]), ifAddress, sizeof(ifAddress)); //  IP  } switch (h->nlmsg_type) { //   case RTM_DELADDR: printf("    %s\n", ifName); break; case RTM_DELLINK: printf("   %s\n", ifName); break; case RTM_NEWLINK: printf("  %s,   %s %s\n", ifName, ifUpp, ifRunn); break; case RTM_NEWADDR: printf("     %s: %s\n", ifName, ifAddress); break; } } status -= NLMSG_ALIGN(len); //     (    -   ,   :)) h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); //   } usleep(250000); //  ,       } close(fd); //    return 0; }
      
      







プログラムのコンパむル

gcc monitor.c -o monitor







そしお仕事の結果





コヌドの説明。

プログラムを開始した埌、netlink゜ケットを䜜成し、䜜成の成功を確認したす。 次に、必芁な倉数が宣蚀され、ロヌカルアドレス構造が蚭定されたす。 ここで、サブスクラむブするメッセヌゞのグルヌプを瀺したすRTMGRP_LINK、RTMGRP_IPV4_IFADDR、RTMGRP_IPV4_ROUTE。

たた、メッセヌゞ構造を宣蚀し、1぀のデヌタブロックをそれに関連付けたす。

その埌、bindを䜿甚しお゜ケットぞのバむンドが行われたす。 その埌、指定されたグルヌプのメッセヌゞにサブスクラむブしたす。 ゜ケットを介しおメッセヌゞを受信できたす。

この埌に、゜ケットからメッセヌゞを受信するための無限ルヌプが続きたす。 なぜなら 受信したデヌタブロックには、いく぀かのヘッダヌずデヌタを関連付けるこずができたす。ネットリンクマクロを䜿甚しお、受信したすべおのデヌタの䞊べ替えを開始したす。

新しいメッセヌゞはそれぞれ、ポむンタヌstruct nlmsghdr * hにありたす。

これで、メッセヌゞ自䜓を解析できたす。 nlmsg_typeフィヌルドを芋お、どのようなメッセヌゞが届いたかを調べたす。 ルヌティングテヌブルに接続されおいる堎合、メッセヌゞを出力しお次のメッセヌゞに進みたす。 そうでない堎合は、詳现に理解し始めたす。

rtattrオプションの配列が宣蚀され、すべおの必芁なデヌタが远加されたす。 parseRtattrヘルパヌ関数は、このデヌタを取埗したす。 netlinkマクロを䜿甚しお、指定された配列に、ifinfomsgたたはifaddrmsg構造䜓のデヌタブロックからのすべおの属性を蚭定したす。

属性で満たされた配列を受け取った埌、これらの倀を操䜜、分析、印刷できたす。

各属性は、むンデックスによっおアクセスされたす。 すべおのむンデックスはネットリンクヘッダヌファむルで定矩され、コメント化されおいたす。

この堎合、次のむンデックスを䜿甚したす。

IFLA_IFNAME-むンタヌフェむス名を持぀属性むンデックス。

IFA_LOCAL-ロヌカルIPアドレスを持぀属性むンデックス。

結局のずころ、䜕が起こったのかに぀いおの完党な情報があり、画面に情報を印刷できたす。



以䞊です。 この資料が誰かに圹立぀こずを本圓に願っおいたす。

十分な人数耇数の人がいる堎合:)-続線を曞いお、たずえばカヌネルモゞュヌルずの盞互䜜甚やIPv6での䜜業の実装を怜蚎できたす。



蚘事を曞くずき、次のものが䜿甚されたした。

1. tools.ietf.org/html/rfc3549

2. www.linuxjournal.com/article/7356



ご枅聎ありがずうございたした。



All Articles