Linuxでの非ポーリング方式によるNIC接続状態の監視

9181 ワード

LinuxでNICの接続状態を監視するには、ポーリング方式で定期的にクエリーしたり、値をアクティブに取得したりするのではなく、NICの接続状態が変化したときにプログラムが通知を受け取る方法がいくつかあります.
stackoverflowの投稿(アドレスは後述)を検索することで手がかりを得ました.
1.AF_の使用NETLINK socket
2.RTMGRP_にバインドLINKグループ
3.受信待ちRTM_NEWLINKとRTM_DELLINKタイプのメッセージ
4.受信したメッセージのifinfomsg構造体のifi_を解析するflagsメンバーがIFF_に設定されているかどうかRUNNING
文字の段落が多すぎるのは見ていると大変ですね.文字をできるだけコードとして注釈しましょう.理論と実践を結びつけましょう.
エンドコード:
#include 
#include 
#include 
#include 
#include 
#include 
#include  /*   `man 7 rtnetlink`,      。(             ,      ,        ,    ,        Linux        ) */
#include 
#include  /* `man 7 rtnetlink`  "ifi_flags  contains  the  device  flags,  see  netdevice(7)"   ,`man 7 netdevice`         。 */
//#include  /* use the following header instead of this one */
#include  /* this one is suite for the Linux, more related to interact with Linux kernel's network interfaces */
#include  /*   ARPHRD_LOOPBACK    */
#include  /*   `man 7 rtnetlink`,       */
#include  /*   `man 7 rtnetlink`,       */

#define PRINTF(fmt, ...) printf("%s:%d: " fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__)

#define BUF_SIZE 4096 /*   `man 7 netlink` reading netlink message  EXAMPLE buf    4096,        。 */

static int init(struct sockaddr_nl *psa);
static int deinit();
static int msg_req();
static int msg_loop(struct msghdr *pmh);
static void sig_handler(int sig);

static int sfd = -1;

int main (int argc, char *argv[])
{
	int ret = 0;
	/*   4       `man 7 netlink` reading netlink message  EXAMPLE, `man 2 recvmsg` "The recvmsg() call uses a msghdr structure to minimize the number of directly supplied arguments"          。 */
	char buf[BUF_SIZE];
	struct iovec iov = {buf, sizeof(buf)};
	struct sockaddr_nl sa;
	struct msghdr msg = {(void *)&sa, sizeof(sa), &iov, 1, NULL, 0, 0};

	/*             4     :
	 * 1.    socket
	 * 2.       link  
	 * 3.       link     
	 * 4.     
	 */
	ret = init(&sa);
	if (!ret) {
		ret = msg_req();
	}
	if (!ret) {
		ret = msg_loop(&msg);
	}
	ret = deinit();

	return ret;
}

static int init(struct sockaddr_nl *psa)
{
	int ret = 0;
	struct sigaction sigact;

	sigact.sa_handler = sig_handler;
	if (!ret && -1 == sigemptyset(&sigact.sa_mask)) {
		PRINTF("ERROR! sigemptyset
"); ret = -1; } if (!ret && -1 == sigaction(SIGINT, &sigact, NULL)) { /* SIGINT Ctrl+C ( ) */ PRINTF("ERROR! sigaction SIGINT
"); ret = -1; } if (!ret && -1 == sigaction(SIGTERM, &sigact, NULL)) { /* SIGINT Ctrl+C ( ) */ PRINTF("ERROR! sigaction SIGTERM
"); ret = -1; } if (!ret) { /* `man 7 netlink` "the netlink protocol does not distinguish between datagram and raw sockets" , socket 2 SOCK_RAW SOCK_DGRAM 。 */ /* `man 7 netlink` netlink_family ,NETLINK_ROUTE netlink_family link 。 */ sfd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (-1 == sfd) { PRINTF("ERROR! socket: %s
", strerror(errno)); ret = -1; } } /* bind `man 7 netlink` EXAMPLE 1 */ memset(psa, 0, sizeof(*psa)); psa->nl_family = AF_NETLINK; psa->nl_groups = RTMGRP_LINK; /* RTMGRP_LINK */ if (!ret && bind(sfd, (struct sockaddr *)psa, sizeof(*psa))) { PRINTF("ERROR! bind: %s
", strerror(errno)); ret = -1; } /* deinit if init failed */ if (0 != ret) { deinit(); } return ret; } static int deinit() { int ret = 0; if (-1 != sfd) { if (-1 == close(sfd)) { PRINTF("ERROR! close: %s
", strerror(errno)); ret = -1; } sfd = -1; } return ret; } static int msg_req() { int ret = 0; struct { struct nlmsghdr nh; struct ifinfomsg ifimsg; } req; /* link , , , */ /* `man 3 rtnetlink` EXAMPLE, http://fossies.org/linux/misc/open-fcoe-3.19.tar.gz/open-fcoe-3.19/fcoe-utils/lib/rtnetlink.c send_getlink_dump */ memset(&req, 0, sizeof(req)); req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); /* NLMSG_LENGTH nlmsghdr header ifinfomsg payload size, */ req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; /* `man 7 netlink` NLM_F_REQUEST , NLM_F_REQUEST; NLM_F_DUMP link , `man 7 netlink`,NLM_F_DUMP NLM_F_ROOT|NLM_F_MATCH,`man 7 netlink` NLM_F_MATCH "Not implemented yet", NLM_F_DUMP, NLM_F_MATCH 。 */ req.nh.nlmsg_type = RTM_GETLINK; /* `man 7 rtnetlink` RTM_GETLINK "get information about a specific network interface",`man 7 rtnetlink` "These messages contain an ifinfomsg structure followed by a series of rtattr structures" RTM_GETLINK */ req.ifimsg.ifi_family = AF_UNSPEC; /* `man 7 rtnetlink` ifinfomsg , AF_UNSPEC */ req.ifimsg.ifi_index = 0; /* http://man7.org/linux/man-pages/man7/rtnetlink.7.html "ifi_index is the unique interface index (since Linux 3.7, it is possible to feed a nonzero value with the RTM_NEWLINK message, thus creating a link with the given ifindex)" , 0 */ req.ifimsg.ifi_change = 0xFFFFFFFF; /* `man 7 rtnetlink` "ifi_change is reserved for future use and should be always set to 0xFFFFFFFF" , 0xFFFFFFFF */ if (-1 == send(sfd, &req, req.nh.nlmsg_len, 0)) { PRINTF("ERROR! send: %s
", strerror(errno)); ret = -1; } return ret; } static int msg_loop(struct msghdr *pmh) { int ret = 0; ssize_t nread = -1; char *buf = (char *)(pmh->msg_iov->iov_base); struct nlmsghdr *nh; struct ifinfomsg *ifimsg; struct rtattr *rta; int attrlen; /* `man 7 netlink` reading netlink message EXAMPLE, :D */ while (!ret) { nread = recvmsg(sfd, pmh, 0); if (-1 == nread) { PRINTF("ERROR! recvmsg: %s
", strerror(errno)); ret = -1; } for (nh = (struct nlmsghdr *)buf; /* `man 7 netlink` "Netlink messages consist of a byte stream with one or multiple nlmsghdr headers and associated payload" netlink , nlmsghdr header */ !ret && NLMSG_OK(nh, nread); /* `man 3 netlink`,NLMSG_OK netlink OK */ nh = NLMSG_NEXT(nh, nread)) { /* `man 3 netlink`,NLMSG_NEXT nlmsghdr header , nread */ /* `man 3 netlink` "The caller must check if the current nlmsghdr didn't have the NLMSG_DONE set—this function doesn't return NULL on end" , NLMSG_DONE */ /* The end of multipart message. */ if (NLMSG_DONE == nh->nlmsg_type) { break; } if (NLMSG_ERROR == nh->nlmsg_type) { /* Do some error handling. */ PRINTF("ERROR! NLMSG_ERROR
"); ret = -1; } /* Continue with parsing payload. */ if (!ret && (RTM_NEWLINK == nh->nlmsg_type || RTM_DELLINK == nh->nlmsg_type)) { /* `man 7 rtnetlink` "These messages contain an ifinfomsg structure followed by a series of rtattr structures" RTM_NEWLINK RTM_DELLINK , RTM_NEWLINK RTM_DELLINK "Create" "remove" "information about a specific network interface",link "Create" "remove" 。 */ ifimsg = (struct ifinfomsg *)NLMSG_DATA(nh); /* `man 3 netlink`,NLMSG_DATA nlmsghdr header payload */ if (ARPHRD_LOOPBACK != ifimsg->ifi_type) { /* loopback */ /* `man 7 rtnetlink` "These messages contain an ifinfomsg structure followed by a series of rtattr structures" ifinfomsg */ attrlen = nh->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); /* NLMSG_LENGTH nlmsghdr ifinfomsg , , */ for (rta = IFLA_RTA(ifimsg); /* IFLA_RTA ifinfomsg payload */ RTA_OK(rta, attrlen) && rta->rta_type <= IFLA_MAX; /* `man 3 rtnetlink`,RTA_OK rta */ rta = RTA_NEXT(rta, attrlen)) { /* `man 3 rtnetlink`,RTA_NEXT , attrlen */ if (IFLA_IFNAME == rta->rta_type) { /* rta_type IFLA_IFNAME , */ printf("%s: ", (char*)RTA_DATA(rta)); /* , eth0 */ } } /* , link up ,IFF_RUNNING IFF_LOWER_UP ,link down , IFF_LOWER_UP , `man 7 netdevice`,IFF_LOWER_UP 2.6.17 , IFF_RUNNING */ if (IFF_RUNNING & ifimsg->ifi_flags) printf("link up
"); else printf("link down
"); } } } } return ret; } static void sig_handler(int sig) { exit(deinit()); }

コードとmanコマンドの実行環境はdebian wheezy 7.7です.
参照先:
http://stackoverflow.com/questions/10340145/get-event-for-nic-ethernet-card-link-status-on-linux
man 3 netlink
man 7 netlink
man 3 rtnetlink
man 7 rtnetlink
man 7 netdevice
http://centaur.sch.bme.hu/~leait/projects/openwrt/
http://fossies.org/linux/misc/open-fcoe-3.19.tar.gz/open-fcoe-3.19/fcoe-utils/lib/rtnetlink.c