From: Ole Reinhardt Date: Sun, 28 Apr 2013 23:37:14 +0000 (+0200) Subject: Added netwatch X-Git-Url: https://git.karo-electronics.de/?p=mdnsd.git;a=commitdiff_plain;h=9246430f3e5fe9630fbd9cc289b6a4bc3d97d8af Added netwatch --- diff --git a/netwatch.c b/netwatch.c new file mode 100755 index 0000000..72320ed --- /dev/null +++ b/netwatch.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "netwatch.h" + +#define NL_BUFSIZE 8192 + + +/* yeah, these callbacks really shouldn't be global */ + +static void *user_data = NULL; +static NW_IpChangeCallback on_ip_change = NULL; +static NW_LinkChangeCallback on_link_change = NULL; + +static void netwatch_handle_ifaddrmsg (struct nlmsghdr *nl_msg); +static void netwatch_handle_ifinfomsg (struct nlmsghdr *nl_msg); + + +int +netwatch_open (void) +{ + int fd; + struct sockaddr_nl local; + + /* Create Socket */ + if ((fd = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) + return -1; + + memset (&local, 0, sizeof (local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; + + if (bind (fd, (struct sockaddr*) &local, sizeof (local)) < 0) + return -1; + + if (fcntl (fd, F_SETFL, O_NONBLOCK)) + return -1; + + return fd; +} + +int +netwatch_queue_inforequest (int fd) +{ + char buf[NLMSG_LENGTH (sizeof (struct ifaddrmsg))]; + struct nlmsghdr *nl_msg; + + bzero (buf, sizeof(buf)); + nl_msg = (struct nlmsghdr *) buf; + + /* For getting interface addresses */ + nl_msg->nlmsg_len = NLMSG_LENGTH (sizeof (struct ifaddrmsg)); + nl_msg->nlmsg_type = RTM_GETADDR; + nl_msg->nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST; + nl_msg->nlmsg_pid = getpid (); + + return write (fd, nl_msg, nl_msg->nlmsg_len); +} + + +int +netwatch_dispatch (int fd) +{ + struct nlmsghdr *nl_msg; + char buf[NL_BUFSIZE]; + int len, count; + + count = 0; + if ((len = recv (fd, buf, NL_BUFSIZE, 0)) < 0) + { + return (errno == EAGAIN) ? 0 : -1; + } + + for (nl_msg = (struct nlmsghdr *) buf; + NLMSG_OK (nl_msg, len); + nl_msg = NLMSG_NEXT (nl_msg, len)) + { + switch (nl_msg->nlmsg_type) + { + case RTM_NEWADDR: + case RTM_DELADDR: + netwatch_handle_ifaddrmsg (nl_msg); + break; + case RTM_NEWLINK: + case RTM_DELLINK: + netwatch_handle_ifinfomsg (nl_msg); + break; + case NLMSG_DONE: + break; + default: + fprintf (stderr, "unhandled message (%d)\n", + nl_msg->nlmsg_type); + break; + } + + count++; + } + return count; +} + + +void +netwatch_register_callbacks (NW_IpChangeCallback ip_cb, + NW_LinkChangeCallback link_cb, + void *userdata) +{ + user_data = userdata; + on_ip_change = ip_cb; + on_link_change = link_cb; +} + + +static void +netwatch_handle_ifaddrmsg (struct nlmsghdr *nl_msg) +{ + struct ifaddrmsg *if_msg; + struct rtattr *attrib; + int len; + + char address[100]; + char *label = NULL; + + if_msg = (struct ifaddrmsg *) NLMSG_DATA (nl_msg); + + if (if_msg->ifa_family != AF_INET) + return; + + address[0] = '\0'; + + len = IFA_PAYLOAD (nl_msg); + for (attrib = IFA_RTA (if_msg); + RTA_OK (attrib, len); + attrib = RTA_NEXT (attrib, len)) + { + switch (attrib->rta_type) + { + case IFA_LOCAL: + inet_ntop (AF_INET, RTA_DATA (attrib), address, sizeof (address)); + break; + + case IFA_LABEL: + label = (char *) RTA_DATA (attrib); + break; + + default: + /* ignore all other attributes */ + break; + } + } + + /* if we got both a label and an IP address */ + if (on_ip_change && label && address[0] && + (nl_msg->nlmsg_type == RTM_NEWADDR || nl_msg->nlmsg_type == RTM_DELADDR)) + { + on_ip_change (if_msg->ifa_index, label, address, + nl_msg->nlmsg_type == RTM_NEWADDR, user_data); + } + + return; +} + + +static void +netwatch_handle_ifinfomsg (struct nlmsghdr *nl_msg) +{ + static unsigned long have_state = 0; + static unsigned long linkstate = 0; + struct ifinfomsg *ifi; + + ifi = (struct ifinfomsg *) NLMSG_DATA (nl_msg); + + if (on_link_change && ifi->ifi_index < sizeof (unsigned long) * 8) + { + if (!((1 << ifi->ifi_index) & have_state) || + ((ifi->ifi_flags & IFF_RUNNING) && !((1 << ifi->ifi_index) & linkstate)) || + (!(ifi->ifi_flags & IFF_RUNNING) && ((1 << ifi->ifi_index) & linkstate))) + { + have_state |= (1 << ifi->ifi_index); + if (ifi->ifi_flags & IFF_RUNNING) + linkstate |= (1 << ifi->ifi_index); + else + linkstate &= ~(1 << ifi->ifi_index); + + on_link_change (ifi->ifi_index, + ifi->ifi_flags & IFF_RUNNING ? 1 : 0, + user_data); + } + } +} + + + +#ifdef NETWATCH_MAIN +void +print_ip_change (int link_index, + char *label, + char *ipaddr, + int add, + void *user_data) +{ + fprintf (stdout, "Link No. %d %s address %s (label %s)\n", + link_index, add ? "got" : "lost", ipaddr, label); +} + + +void print_link_change (int link_index, + int running, + void *user_data) +{ + fprintf (stdout, "Link No. %d is %sconnected\n", + link_index, running ? "" : "no longer "); +} + + +int +main (int argc, char *argv[]) +{ + struct pollfd pollfd; + int fd; + + netwatch_register_callbacks (print_ip_change, print_link_change, NULL); + + fd = netwatch_open (); + if (fd < 0) + { + perror ("Error opening netlink socket"); + exit (1); + } + + netwatch_queue_inforequest (fd); + + while (1) + { + pollfd.fd = fd; + pollfd.events = POLLIN; + pollfd.revents = 0; + + if (poll (&pollfd, 1, 20000)) + netwatch_dispatch (fd); + } + + close (fd); + return 0; +} +#endif diff --git a/netwatch.h b/netwatch.h new file mode 100755 index 0000000..e33c029 --- /dev/null +++ b/netwatch.h @@ -0,0 +1,22 @@ +#ifndef __NETWATCH_H__ +#define __NETWATCH_H__ + +typedef void (* NW_IpChangeCallback) (int link_index, + char *label, + char *ipaddr, + int add, + void *user_data); + +typedef void (* NW_LinkChangeCallback) (int link_index, + int running, + void *user_data); + +int netwatch_open (void); +int netwatch_queue_inforequest (int fd); +int netwatch_dispatch (int fd); + +void netwatch_register_callbacks (NW_IpChangeCallback ip_cb, + NW_LinkChangeCallback link_cb, + void *userdata); + +#endif /* __NETWATCH_H__ */