diff options
Diffstat (limited to 'ctrl/facemgr/src/interfaces/netlink')
-rw-r--r-- | ctrl/facemgr/src/interfaces/netlink/netlink.c | 472 |
1 files changed, 369 insertions, 103 deletions
diff --git a/ctrl/facemgr/src/interfaces/netlink/netlink.c b/ctrl/facemgr/src/interfaces/netlink/netlink.c index 5bf0baf9f..08cbe4d3b 100644 --- a/ctrl/facemgr/src/interfaces/netlink/netlink.c +++ b/ctrl/facemgr/src/interfaces/netlink/netlink.c @@ -18,34 +18,120 @@ * \brief Netlink interface */ +#include <assert.h> #include <linux/rtnetlink.h> +#include <net/if_arp.h> // ARPHRD_LOOPBACK #include <sys/types.h> // getpid #include <unistd.h> // getpid -#include "../../event.h" -#include "../../facemgr.h" +#include <hicn/facemgr.h> +#include <hicn/util/ip_address.h> +#include <hicn/util/log.h> + +#include "../../common.h" +#include "../../facelet.h" #include "../../interface.h" +typedef enum { + NL_STATE_UNDEFINED, + NL_STATE_LINK_SENT, + NL_STATE_ADDR_SENT, + NL_STATE_DONE, +} nl_state_t; + /* Internal data storage */ typedef struct { int fd; + nl_state_t state; } nl_data_t; -// little helper to parsing message using netlink macroses -void parseRtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +static inline void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len, + unsigned short flags) { + unsigned short type; + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + type = rta->rta_type & ~flags; + if (type <= max) + tb[type] = rta; + rta = RTA_NEXT(rta, len); + } +} + +int nl_process_state(interface_t * interface) +{ + nl_data_t * data = (nl_data_t*)interface->data; + int rc; + + switch(data->state) { + case NL_STATE_UNDEFINED: + { + struct { + struct nlmsghdr header; + struct rtgenmsg payload; + } msg2 = { + .header = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + .nlmsg_type = RTM_GETLINK, + .nlmsg_pid = getpid(), + .nlmsg_seq = 3, + }, + .payload = { + .rtgen_family = AF_PACKET, + } + }; + + rc = send(data->fd, &msg2, msg2.header.nlmsg_len, 0); + if (rc < 0) + printf("E: Error sending netlink query\n"); + + data->state = NL_STATE_LINK_SENT; + break; + } + + case NL_STATE_LINK_SENT: + { + /* Issue a first query to receive static state */ + struct { + struct nlmsghdr header; + struct ifaddrmsg payload; + } msg = { + .header = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), + .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + .nlmsg_type = RTM_GETADDR, + .nlmsg_pid = getpid(), + .nlmsg_seq = 7, + }, + .payload = { + .ifa_family = AF_INET, + } + }; + + rc = send(data->fd, &msg, msg.header.nlmsg_len, 0); + if (rc < 0) + printf("E: Error sending netlink query\n"); + + data->state = NL_STATE_ADDR_SENT; + break; + } - while (RTA_OK(rta, len)) { // while not end of the message - if (rta->rta_type <= max) { - tb[rta->rta_type] = rta; // read attr + case NL_STATE_ADDR_SENT: + { + data->state = NL_STATE_DONE; + break; } - rta = RTA_NEXT(rta,len); // get next attr + + default: /* NL_STATE_DONE never called */ + break; } -} + return 0; +} -int nl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata) +int nl_initialize(interface_t * interface, void * cfg) { nl_data_t * data = malloc(sizeof(nl_data_t)); if (!data) @@ -57,31 +143,194 @@ int nl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata) goto ERR_SOCKET; } + data->state = NL_STATE_UNDEFINED; + struct sockaddr_nl local; // local addr struct memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; // set protocol family - local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; // set groups we interested in + // NOTE: RTNLGRP_LINK replaces obsolete RTMGRP_LINK, etc + local.nl_groups = 0 + | RTMGRP_LINK + | RTMGRP_IPV4_IFADDR + | RTMGRP_IPV6_IFADDR +#if 0 + | RTMGRP_IPV4_ROUTE; + | RTMGRP_IPV6_ROUTE; +#endif + ; local.nl_pid = getpid(); // set out id using current process id - if (bind(data->fd, (struct sockaddr*)&local, sizeof(local)) < 0) { // bind socket printf("Failed to bind netlink socket: %s\n", (char*)strerror(errno)); goto ERR_BIND; } - /* Issue a first query to receive static state */ + interface->data = data; + nl_process_state(interface); - *pdata = data; - return data->fd; // FACEMGR_SUCCESS; + return data->fd; // 0; ERR_BIND: close(data->fd); ERR_SOCKET: free(data); ERR_MALLOC: - *pdata = NULL; - return FACEMGR_FAILURE; + return -1; +} + +int parse_link(struct nlmsghdr * h, facelet_t ** facelet, + char * interface_name, size_t interface_name_size, + bool * up, bool * running) +{ + struct ifinfomsg *ifi; // structure for network interface info + struct rtattr *tb[IFLA_MAX + 1]; + + assert(facelet); + + ifi = (struct ifinfomsg*) NLMSG_DATA(h); // get information about changed network interface + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(h), 1<<15); + + if (interface_name) { + assert(tb[IFLA_IFNAME]); + snprintf(interface_name, interface_name_size, "%s", (char*)RTA_DATA(tb[IFLA_IFNAME])); + } + + if (up) + *up = ifi->ifi_flags & IFF_UP; + if (running) + *running = ifi->ifi_flags & IFF_RUNNING; + + + *facelet = facelet_create(); + netdevice_t * netdevice = netdevice_create_from_name(interface_name); + if (!netdevice) + goto ERROR; + int rc = facelet_set_netdevice(*facelet, *netdevice); + if (rc < 0) + goto ERROR; + +// FIXME Tags +#if 0 + /* This is the only opportunity to identify a loopback interface on both + * linux _and_ android, as NetworkCapabilities does not have a flag for + * LOOPBACK... */ + if (ifi->ifi_type==ARPHRD_LOOPBACK) { + DEBUG("loopback"); + } + +#ifdef IFLA_WIRELESS + /* + * This signals a wirless event, but it typically occurs _after_ a face is + * created... we might need to update an existing face by setting a tag... + * or find a way to exploit this flag before actually creating the face... + */ + if (tb[IFLA_WIRELESS]) + DEBUG("wireless!!!"); +#else +#warning "IFLA_WIRELESS not supported on this platform" +#endif /* IFLA_WIRELESS */ + +#endif + + // TODO + // - ifi_change + // - IFLA_PROTINFO + + return 0; + +ERROR: + facelet_free(*facelet); + *facelet = NULL; + + return -1; +} + +int parse_addr(struct nlmsghdr * h, facelet_t ** facelet, + char * interface_name, size_t interface_name_size, + char * interface_address, size_t interface_address_size) +{ + ip_address_t local_addr = IP_ADDRESS_EMPTY; + struct ifaddrmsg *ifa; // structure for network interface data + struct rtattr *tba[IFA_MAX+1]; + + assert(facelet); + + ifa = (struct ifaddrmsg*)NLMSG_DATA(h); // get data from the network interface + + parse_rtattr(tba, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(h), 0); + + /* FIXME + * + * IFA_LOCAL ok for v4, not there for v6 + * + * IFA_ADDRESS seems to work for both but with the following precaution + * + * IFA_ADDRESS is prefix address, rather than local interface address. + * It makes no difference for normally configured broadcast interfaces, + * but for point-to-point IFA_ADDRESS is DESTINATION address, + * local address is supplied in IFA_LOCAL attribute. + */ + if (!tba[IFA_ADDRESS]) { + ERROR("[netlink.parse_addr] No local address"); + return -1; + } + + switch(ifa->ifa_family) { + case AF_INET: + local_addr.v4.as_inaddr = *(struct in_addr*)RTA_DATA(tba[IFA_ADDRESS]); + break; + case AF_INET6: + local_addr.v6.as_in6addr = *(struct in6_addr*)RTA_DATA(tba[IFA_ADDRESS]); + break; + default: + return 0; + } + +#if 0 /* Not working for v6 */ + if (interface_name) { + assert(tba[IFLA_IFNAME]); + snprintf(interface_name, interface_name_size, "%s", (char*)RTA_DATA(tba[IFLA_IFNAME])); + } +#endif + + /* See comment in parse_link */ + if (interface_address) { + assert(tba[IFA_ADDRESS]); + ip_address_snprintf(interface_address, interface_address_size, &local_addr, ifa->ifa_family); + } + + netdevice_t * netdevice = netdevice_create_from_index(ifa->ifa_index); + if (!netdevice) { + ERROR("[netlink.parse_addr] error creating netdevice '%s'", interface_name); + goto ERROR; + } + + if (interface_name) { + snprintf(interface_name, interface_name_size, "%s", netdevice->name); + } + + *facelet = facelet_create(); + if (facelet_set_netdevice(*facelet, *netdevice) < 0) { + ERROR("[netlink.parse_addr] error setting netdevice"); + goto ERROR; + } + if (facelet_set_family(*facelet, ifa->ifa_family) < 0) { + ERROR("[netlink.parse_addr] error setting family"); + goto ERROR; + } + if (facelet_set_local_addr(*facelet, local_addr) < 0) { + ERROR("[netlink.parse_addr] error setting local address"); + goto ERROR; + } + + return 0; + +ERROR: + facelet_free(*facelet); + *facelet = NULL; + + return -1; } int nl_callback(interface_t * interface) @@ -97,13 +346,12 @@ int nl_callback(interface_t * interface) iov.iov_len = sizeof(buf); // set size // initialize protocol message header - struct msghdr msg; - { - msg.msg_name = &local; // local address - msg.msg_namelen = sizeof(local); // address size - msg.msg_iov = &iov; // io vector - msg.msg_iovlen = 1; // io size - } + struct msghdr msg = { + .msg_name = &local, // local address + .msg_namelen = sizeof(local), // address size + .msg_iov = &iov, // io vector + .msg_iovlen = 1, // io size + }; ssize_t status = recvmsg(data->fd, &msg, 0); @@ -115,12 +363,12 @@ int nl_callback(interface_t * interface) */ printf("Failed to read netlink: %s", (char*)strerror(errno)); - return FACEMGR_FAILURE; + return -1; } if (msg.msg_namelen != sizeof(local)) { // check message length, just in case printf("Invalid length of the sender address struct\n"); - return FACEMGR_FAILURE; + return -1; } // message parser @@ -129,103 +377,122 @@ int nl_callback(interface_t * interface) for (h = (struct nlmsghdr*)buf; status >= (ssize_t)sizeof(*h); ) { // read all messagess headers int len = h->nlmsg_len; int l = len - sizeof(*h); - char *ifName = NULL; if ((l < 0) || (len > status)) { printf("Invalid message length: %i\n", len); continue; } - // now we can check message type - if ((h->nlmsg_type == RTM_NEWROUTE) || (h->nlmsg_type == RTM_DELROUTE)) { // some changes in routing table - printf("Routing table was changed\n"); - } else { // in other case we need to go deeper - char *ifUpp; - char *ifRunn; - struct ifinfomsg *ifi; // structure for network interface info - struct rtattr *tb[IFLA_MAX + 1]; - - ifi = (struct ifinfomsg*) NLMSG_DATA(h); // get information about changed network interface - - parseRtattr(tb, IFLA_MAX, IFLA_RTA(ifi), h->nlmsg_len); // get attributes - - if (tb[IFLA_IFNAME]) { // validation - ifName = (char*)RTA_DATA(tb[IFLA_IFNAME]); // get network interface name - } - - if (ifi->ifi_flags & IFF_UP) { // get UP flag of the network interface - ifUpp = (char*)"UP"; - } else { - ifUpp = (char*)"DOWN"; - } + switch(h->nlmsg_type) { +#if 0 + case RTM_NEWROUTE: + case RTM_DELROUTE: + DEBUG("Routing table was changed"); + break; +#endif + + case RTM_DELADDR: + { + facelet_t * facelet = NULL; + char interface_name[IFNAMSIZ]; + char interface_address[MAXSZ_IP_ADDRESS] = {0}; + + if (parse_addr(h, &facelet, interface_name, IFNAMSIZ, + interface_address, MAXSZ_IP_ADDRESS) < 0) { + ERROR("Error parsing address message"); + break; + } - if (ifi->ifi_flags & IFF_RUNNING) { // get RUNNING flag of the network interface - ifRunn = (char*)"RUNNING"; - } else { - ifRunn = (char*)"NOT RUNNING"; + DEBUG("Interface %s: address was removed", interface_name); + if (facelet) { + facelet_set_event(facelet, FACELET_EVENT_DELETE); + facelet_raise_event(facelet, interface); + } + break; } - char ifAddress[256] = {0}; // network addr - struct ifaddrmsg *ifa; // structure for network interface data - struct rtattr *tba[IFA_MAX+1]; + case RTM_NEWADDR: + { + facelet_t * facelet = NULL; + char interface_name[IFNAMSIZ]; + char interface_address[MAXSZ_IP_ADDRESS] = {0}; - ifa = (struct ifaddrmsg*)NLMSG_DATA(h); // get data from the network interface + if (parse_addr(h, &facelet, interface_name, IFNAMSIZ, + interface_address, MAXSZ_IP_ADDRESS) < 0) { + ERROR("Error parsing address message"); + break; + } - parseRtattr(tba, IFA_MAX, IFA_RTA(ifa), h->nlmsg_len); + DEBUG("Interface %s: new address was assigned: %s", interface_name, interface_address); - if (tba[IFA_LOCAL]) { - inet_ntop(AF_INET, RTA_DATA(tba[IFA_LOCAL]), ifAddress, sizeof(ifAddress)); // get IP addr + if (facelet) { + facelet_set_event(facelet, FACELET_EVENT_UPDATE); + facelet_raise_event(facelet, interface); + } + break; } - face_t * face; - - if (tba[IFA_LOCAL]) { - ip_address_t local_addr = IP_ADDRESS_EMPTY; - switch(ifa->ifa_family) { - case AF_INET: - local_addr.v4.as_inaddr = *(struct in_addr*)RTA_DATA(tba[IFA_LOCAL]); - break; - case AF_INET6: - local_addr.v6.as_in6addr = *(struct in6_addr*)RTA_DATA(tba[IFA_LOCAL]); - break; - default: - continue; + case RTM_DELLINK: + { + facelet_t * facelet = NULL; + char interface_name[IFNAMSIZ]; + if (parse_link(h, &facelet, interface_name, IFNAMSIZ, + NULL, NULL) < 0) { + ERROR("Error parsing link message"); + break; + } + if (facelet) { + facelet_set_event(facelet, FACELET_EVENT_DELETE); + facelet_raise_event(facelet, interface); } - face = face_create_udp(&local_addr, 0, &IP_ADDRESS_EMPTY, 0, ifa->ifa_family); - } else { - face = NULL; + + DEBUG("Network interface %s was removed", interface_name); + break; } - switch (h->nlmsg_type) { - case RTM_DELADDR: - // DOES NOT SEEM TO BE TRIGGERED - printf("Interface %s: address was removed\n", ifName); - if (face) - event_raise(EVENT_TYPE_DELETE, face, interface); - break; + case RTM_NEWLINK: + { + facelet_t * facelet = NULL; + char interface_name[IFNAMSIZ]; + bool up, running; - case RTM_DELLINK: - printf("Network interface %s was removed\n", ifName); + if (parse_link(h, &facelet, interface_name, IFNAMSIZ, &up, &running) < 0) { + ERROR("Error parsing link message"); break; + } - case RTM_NEWLINK: - printf("New network interface %s, state: %s %s\n", ifName, ifUpp, ifRunn); - // UP RUNNING - // UP NOT RUNNING - // DOWN NOT RUNNING - if (!(ifi->ifi_flags & IFF_UP) || (!(ifi->ifi_flags & IFF_RUNNING))) { - if(face) - event_raise(EVENT_TYPE_DELETE, face, interface); + // UP RUNNING + // UP NOT RUNNING + // DOWN NOT RUNNING + DEBUG("New network interface %s, state: %s %s", interface_name, + up ? "UP" : "DOWN", + running ? "RUNNING" : "NOT_RUNNING"); + + if (facelet && up && running) { + facelet_set_event(facelet, FACELET_EVENT_CREATE); + facelet_t * facelet6 = facelet_dup(facelet); + if (!facelet6) { + ERROR("Could not duplicate face for v6"); + break; } - break; - case RTM_NEWADDR: - printf("Interface %s: new address was assigned: %s\n", ifName, ifAddress); - printf("NEW FACE\n"); - if (face) - event_raise(EVENT_TYPE_CREATE, face, interface); - break; + facelet_set_family(facelet, AF_INET); + facelet_raise_event(facelet, interface); + + facelet_set_family(facelet6, AF_INET6); + facelet_raise_event(facelet6, interface); + } + break; } + + case NLMSG_ERROR: + break; + case NLMSG_DONE: + nl_process_state(interface); + break; + default: + break; + } status -= NLMSG_ALIGN(len); // align offsets by the message length, this is important @@ -233,20 +500,19 @@ int nl_callback(interface_t * interface) h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); // get next message } - return FACEMGR_SUCCESS; + return 0; } int nl_finalize(interface_t * interface) { nl_data_t * data = (nl_data_t*)interface->data; close(data->fd); - return FACEMGR_SUCCESS; + return 0; } const interface_ops_t netlink_ops = { .type = "netlink", - .is_singleton = true, .initialize = nl_initialize, .callback = nl_callback, .finalize = nl_finalize, |