From d459c37901953adf009b6d515d2ab5bd0e67120d Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Thu, 18 Apr 2013 16:08:38 +0200 Subject: this is kind of a complete rewrite... Can display IP address information and should be a lot more reliable. --- netlink-notify.c | 309 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 208 insertions(+), 101 deletions(-) diff --git a/netlink-notify.c b/netlink-notify.c index cabde0f..31e6b87 100644 --- a/netlink-notify.c +++ b/netlink-notify.c @@ -5,20 +5,27 @@ * of the GNU General Public License, incorporated herein by reference. */ +#include +#include +#include #include #include -#include #include -#include - -#include +#include #include -#include +#include #include +#include +#include + +#include #define PROGNAME "netlink-notify" +#define MYPROTO NETLINK_ROUTE +#define MYMGRP RTMGRP_IPV4_ROUTE + #define NOTIFICATION_TIMEOUT 10000 #ifndef DEBUG #define DEBUG 0 @@ -27,80 +34,199 @@ #define ICON_NETWORK_CONNECTED "netlink-notify-connected" #define ICON_NETWORK_DISCONNECTED "netlink-notify-disconnected" -#define TEXT_TOPIC "Netlink Notification" -#define TEXT_NOTIFICATION "Interface %s is %s." -#define TEXT_NOTIFICATION_DEBUG "%s: Interface %s (index %d) is %s.\n" +#define TEXT_TOPIC "Netlink Notification" +#define TEXT_NEWLINK "Interface %s is %s." +#define TEXT_NEWADDR "Interface %s is %s,\nnew address %s/%d." +#define TEXT_DELLINK "An interface has gone away." -// we need these to be global... -unsigned int netlinksize = 1; // never use 0 and avoid overwriting the main pointer... -size_t * notificationref; -char * program = NULL; +#define TEXT_NONE "(NONE)" -static int data_attr_cb(const struct nlattr * attr, void * data) { - const struct nlattr ** tb = data; - int type = mnl_attr_get_type(attr); +#define CHECK_CONNECTED IFF_LOWER_UP - if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) - return MNL_CB_OK; +char *program; +unsigned int maxinterface = 0; +NotifyNotification ** notification; + +/*** newstr_link ***/ +char * newstr_link(char *text, char *interface, unsigned int flags) { + char *notifystr; - switch(type) { - case IFLA_MTU: - if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { - fprintf(stderr, "%s: Invalid netlink attribute.\n", program); - return MNL_CB_ERROR; - } - break; - case IFLA_IFNAME: - if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { - fprintf(stderr, "%s: Invalid netlink attribute.\n", program); - return MNL_CB_ERROR; - } - break; - } - tb[type] = attr; - return MNL_CB_OK; + notifystr = malloc(strlen(text) + strlen(interface) + 4); + sprintf(notifystr, text, interface, (flags & CHECK_CONNECTED) ? "up" : "down"); + + return notifystr; } -static int data_cb(const struct nlmsghdr * nlh, void * data) { - struct nlattr * tb[IFLA_MAX + 1] = {}; - struct ifinfomsg * ifm = mnl_nlmsg_get_payload(nlh); +/*** newstr_addr ***/ +char * newstr_addr(char *text, char *interface, unsigned int flags, unsigned char family, void *ipaddr, unsigned char prefix) { + char *notifystr; + char buf[64]; - char * notifystr = NULL; - const char * interface = NULL; - NotifyNotification * notification = NULL; - unsigned int errcount = 0; + inet_ntop(family, ipaddr, buf, sizeof(buf)); + notifystr = malloc(strlen(text) + strlen(interface) + strlen(buf)); + sprintf(notifystr, text, interface, (flags & CHECK_CONNECTED) ? "up" : "down", buf, prefix); - gboolean res = FALSE; - GError * error = NULL; + return notifystr; +} - mnl_attr_parse(nlh, sizeof(* ifm), data_attr_cb, tb); +/*** newstr_away ***/ +char * newstr_away(char *text) { + char *notifystr; - interface = mnl_attr_get_str(tb[IFLA_IFNAME]); - notifystr = malloc(strlen(interface) + strlen(TEXT_NOTIFICATION) + 1); // 2* %s is enough for "down", but we need an additional byte for \n - sprintf(notifystr, TEXT_NOTIFICATION, interface, (ifm->ifi_flags & IFF_RUNNING ? "up" : "down")); -#if DEBUG - printf(TEXT_NOTIFICATION_DEBUG, program, interface, ifm->ifi_index, (ifm->ifi_flags & IFF_RUNNING ? "up" : "down")); -#endif + notifystr = malloc(strlen(text)); + sprintf(notifystr, text); + + return notifystr; +} + +/*** open_netlink ***/ +int open_netlink (void) { + int sock = socket (AF_NETLINK, SOCK_RAW, MYPROTO); + struct sockaddr_nl addr; + + memset ((void *) &addr, 0, sizeof (addr)); + + if (sock < 0) + return sock; + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid (); + addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; + if (bind (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) + return -1; + return sock; +} - if (netlinksize < ifm->ifi_index) { - notificationref = realloc(notificationref, (ifm->ifi_index + 1) * sizeof(size_t)); - while(netlinksize < ifm->ifi_index) - notificationref[++netlinksize] = 0; +/*** read_event ***/ +int read_event (int sockint, int (*msg_handler) (struct sockaddr_nl *, struct nlmsghdr *)) { + int status; + int ret = 0; + char buf[4096]; + struct iovec iov = { buf, sizeof buf }; + struct sockaddr_nl snl; + struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 }; + struct nlmsghdr *h; + + status = recvmsg (sockint, &msg, 0); + + if (status < 0) { + /* Socket non-blocking so bail out once we have read everything */ + if (errno == EWOULDBLOCK || errno == EAGAIN) + return ret; + + /* Anything else is an error */ + fprintf (stderr, "read_netlink: Error recvmsg: %d\n", status); + return status; } - - if (notificationref[ifm->ifi_index] == 0) { - notification = notify_notification_new(TEXT_TOPIC, notifystr, (ifm->ifi_flags & IFF_RUNNING ? ICON_NETWORK_CONNECTED : ICON_NETWORK_DISCONNECTED)); - notificationref[ifm->ifi_index] = (size_t)notification; - } else { - notification = (NotifyNotification *)notificationref[ifm->ifi_index]; - notify_notification_update(notification, TEXT_TOPIC, notifystr, (ifm->ifi_flags & IFF_RUNNING ? ICON_NETWORK_CONNECTED : ICON_NETWORK_DISCONNECTED)); + + if (status == 0) + fprintf (stderr, "read_netlink: EOF\n"); + + /* We need to handle more than one message per 'recvmsg' */ + for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int) status); h = NLMSG_NEXT (h, status)) { + /* Finish reading */ + if (h->nlmsg_type == NLMSG_DONE) + return ret; + + /* Message is some kind of error */ + if (h->nlmsg_type == NLMSG_ERROR) { + fprintf (stderr, "read_netlink: Message is an error - decode TBD\n"); + return -1; + } + + /* Call message handler */ + if (msg_handler) { + ret = (*msg_handler) (&snl, h); + if (ret < 0) { + fprintf (stderr, "read_netlink: Message hander error %d\n", ret); + return ret; + } + } else { + fprintf (stderr, "read_netlink: Error NULL message handler\n"); + return -1; + } } - notify_notification_set_timeout(notification, NOTIFICATION_TIMEOUT); - notify_notification_set_category(notification, PROGNAME); - notify_notification_set_urgency(notification, NOTIFY_URGENCY_NORMAL); + return ret; +} - while(!notify_notification_show(notification, &error)) { +/*** msg_handler ***/ +static int msg_handler (struct sockaddr_nl *nl, struct nlmsghdr *msg) { + char *notifystr = NULL; + unsigned int errcount = 0; + GError *error = NULL; + struct ifaddrmsg *ifa; + struct ifinfomsg *ifi; + struct rtattr *rth; + int rtl; + char name[IFNAMSIZ]; + + ifa = (struct ifaddrmsg *) NLMSG_DATA (msg); + ifi = (struct ifinfomsg *) NLMSG_DATA (msg); + + if_indextoname(ifi->ifi_index, name); + + /* make sure we have alloced memory for the struct array */ + if (maxinterface < ifi->ifi_index) { + notification = realloc(notification, (ifi->ifi_index + 1) * sizeof(NotifyNotification)); + for(; maxinterface < ifi->ifi_index; maxinterface++) + notification[maxinterface] = NULL; + } + + switch (msg->nlmsg_type) { + /* just return for cases we want to ignore */ + case RTM_NEWADDR: + rth = IFA_RTA (ifa); + rtl = IFA_PAYLOAD (msg); + + while (rtl && RTA_OK (rth, rtl)) { + if (rth->rta_type == IFA_LOCAL) + notifystr = newstr_addr(TEXT_NEWADDR, name, ifi->ifi_flags, + ifa->ifa_family, RTA_DATA (rth), ifa->ifa_prefixlen); + rth = RTA_NEXT (rth, rtl); + } + if (notifystr == NULL) { + return 0; + } +#if DEUBG + puts (notifystr); +#endif + break; + case RTM_DELADDR: + return 0; + case RTM_NEWROUTE: + return 0; + case RTM_DELROUTE: + return 0; + case RTM_NEWLINK: + notifystr = newstr_link(TEXT_NEWLINK, name, ifi->ifi_flags); +#if DEBUG + puts (notifystr); +#endif + break; + case RTM_DELLINK: + notifystr = newstr_away(TEXT_DELLINK); +#if DEBUG + puts (notifystr); +#endif + break; + default: + /* we should not get here... */ + fprintf(stderr, "msg_handler: Unknown netlink nlmsg_type %d\n", msg->nlmsg_type); + return 0; + } + + if (notification[ifi->ifi_index] == NULL) + notification[ifi->ifi_index] = notify_notification_new(TEXT_TOPIC, notifystr, + (ifi->ifi_flags & CHECK_CONNECTED ? ICON_NETWORK_CONNECTED : ICON_NETWORK_DISCONNECTED)); + else + notify_notification_update(notification[ifi->ifi_index], TEXT_TOPIC, notifystr, + (ifi->ifi_flags & CHECK_CONNECTED ? ICON_NETWORK_CONNECTED : ICON_NETWORK_DISCONNECTED)); + + notify_notification_set_timeout(notification[ifi->ifi_index], NOTIFICATION_TIMEOUT); + notify_notification_set_category(notification[ifi->ifi_index], PROGNAME); + notify_notification_set_urgency(notification[ifi->ifi_index], NOTIFY_URGENCY_NORMAL); + + while (!notify_notification_show (notification[ifi->ifi_index], &error)) { if (errcount > 1) { fprintf(stderr, "%s: Looks like we can not reconnect to notification daemon... Exiting.\n", program); exit(EXIT_FAILURE); @@ -108,64 +234,45 @@ static int data_cb(const struct nlmsghdr * nlh, void * data) { g_printerr("%s: Error \"%s\" while trying to show notification. Trying to reconnect.\n", program, error->message); errcount++; - g_error_free(error); + g_error_free (error); error = NULL; - notify_uninit(); + notify_uninit (); - usleep(500 * 1000); + usleep (500 * 1000); - if(!notify_init(PROGNAME)) { + if (!notify_init (PROGNAME)) { fprintf(stderr, "%s: Can't create notify.\n", program); exit(EXIT_FAILURE); } } } errcount = 0; - free(notifystr); - return MNL_CB_OK; + return 0; } -int main(int argc, char ** argv) { - struct mnl_socket * nl; - char buf[MNL_SOCKET_BUFFER_SIZE]; - int ret; +/*** main ***/ +int main (int argc, char **argv) { + int nls; program = argv[0]; + printf ("%s: %s v%s (compiled: " __DATE__ ", " __TIME__ ")\n", argv[0], PROGNAME, VERSION); - printf("%s: %s v%s (compiled: " __DATE__ ", " __TIME__ ")\n", argv[0], PROGNAME, VERSION); - - if(!notify_init(PROGNAME)) { - fprintf(stderr, "%s: Can't create notify.\n", argv[0]); - exit(EXIT_FAILURE); + nls = open_netlink (); + if (nls < 0) { + fprintf (stderr, "%s: Error opening netlin socket!", argv[0]); + exit (EXIT_FAILURE); } - nl = mnl_socket_open(NETLINK_ROUTE); - if (!nl) { - fprintf(stderr, "%s: Can't create netlink socket.\n", argv[0]); - exit(EXIT_FAILURE); - } - - if (mnl_socket_bind(nl, RTMGRP_LINK, MNL_SOCKET_AUTOPID) < 0) { - fprintf(stderr, "%s: Can't bind netlink socket.\n", argv[0]); - exit(EXIT_FAILURE); - } - - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - fprintf(stderr, "%s: An error occured reading from netlink socket.\n", argv[0]); - exit(EXIT_FAILURE); + if (!notify_init (PROGNAME)) { + fprintf (stderr, "%s: Can't create notify.\n", argv[0]); + exit (EXIT_FAILURE); } - mnl_socket_close(nl); + while (1) + read_event (nls, msg_handler); return EXIT_SUCCESS; } -- cgit v1.2.3-70-g09d2