/* * nicutils.c * * Created on: 16 Aug 2012 * Author: daniel */ #include "nicutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUFSIZE 8192 static int netlink_createsock() { return socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); } static int netlink_sendmessage(int sock, struct nlmsghdr* msg) { return send(sock, msg, msg->nlmsg_len, 0); } static int netlink_readsock(int sockFd, char *bufPtr, int seqNum, int pId, int buffsize) { struct nlmsghdr *nlHdr; int readLen = 0, msgLen = 0; do { /* Receive response from the kernel */ if ((readLen = recv(sockFd, bufPtr, buffsize - msgLen, 0)) < 0) { return -1; } nlHdr = (struct nlmsghdr *) bufPtr; /* Check if the header is valid */ if ((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR)) { return -1; } /* Check if the its the last message */ if (nlHdr->nlmsg_type == NLMSG_DONE) { break; } else { /* Else move the pointer to buffer appropriately */ bufPtr += readLen; msgLen += readLen; } /* Check if its a multi part message */ if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) { break; } } while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId)); return msgLen; } static bool route_finddefault(struct nlmsghdr *nlHdr, char* ifname) { // unwrap the data struct rtmsg* rtMsg = (struct rtmsg *) NLMSG_DATA(nlHdr); /* If the route is not for AF_INET or does not belong to main routing table then return. */ if ((rtMsg->rtm_family != AF_INET) || (rtMsg->rtm_table != RT_TABLE_MAIN)) { return NULL; } /* get the rtattr field */ struct rtattr* rtAttr = (struct rtattr *) RTM_RTA(rtMsg); int rtLen = RTM_PAYLOAD(nlHdr); // the idea here is that this doesn't get touched if the // dst is 0 as the response doesn't contain an RTA_DST attribute u_int dst = 0; // bash through the attributes for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen)) { switch (rtAttr->rta_type) { case RTA_OIF: if_indextoname(*(int *) RTA_DATA(rtAttr), ifname); break; case RTA_DST: dst = *((u_int*) RTA_DATA(rtAttr)); break; } // We already know we don't want this route, so get out of here if (dst != 0) { break; } } return dst == 0; } static char* route_defaultrouteint() { int sock, len, msgSeq = 0; /* Create Socket */ if ((sock = netlink_createsock()) < 0) { goto exit; } /* Initialize the buffer */ char* msgBuf = calloc(BUFSIZE, 1); if (msgBuf == NULL) { goto exit; } /* point the header and the msg structure pointers into the buffer */ struct nlmsghdr* nlMsg = (struct nlmsghdr *) msgBuf; struct rtmsg* rtMsg = (struct rtmsg *) NLMSG_DATA(nlMsg); /* Fill in the nlmsg header*/ nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); // Length of message. nlMsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table . nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump. nlMsg->nlmsg_seq = msgSeq++; // Sequence of the message packet. nlMsg->nlmsg_pid = getpid(); // PID of process sending the request. /* Send the request */ if (netlink_sendmessage(sock, nlMsg) < 0) { goto exit; } /* Read the response */ if ((len = netlink_readsock(sock, msgBuf, msgSeq, getpid(), BUFSIZE)) < 0) { goto exit; } static char ifname[256]; for (; NLMSG_OK(nlMsg,len); nlMsg = NLMSG_NEXT(nlMsg,len)) { if (route_finddefault(nlMsg, ifname)) { break; } } exit: if (sock >= 0) { close(sock); } if (msgBuf != NULL) { free(msgBuf); } return ifname; } static bool nicutils_info(char* intefacename, nicinfo* result) { bool ret = false; // get a socket to do the ioctl requests with int sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) { goto exit; } // setup an ifconf struct, allocate a buffer for the data and get // all of the interfaces attached to the system that are up struct ifconf interfaces; interfaces.ifc_len = sizeof(struct ifreq) * 32; // enough space for 32 interfaces.. more than enough interfaces.ifc_buf = malloc(interfaces.ifc_len); if (ioctl(sock, SIOCGIFCONF, &interfaces) < 0) { goto exit; } struct ifreq *ifr = interfaces.ifc_req; int i; for (i = 0; i < interfaces.ifc_len / sizeof(struct ifreq); i++) { // is this the interface we want? if (strcmp(ifr[i].ifr_name, intefacename) != 0) { continue; // skip over this one } // get the address struct sockaddr* addr = &(ifr[i].ifr_addr); // handle ipv4 and ipv6 addresses.. switch (addr->sa_family) { case AF_INET: inet_ntop(AF_INET, &(((struct sockaddr_in *) addr)->sin_addr), result->ip, INET6_ADDRSTRLEN); break; case AF_INET6: inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) addr)->sin6_addr), result->ip, INET6_ADDRSTRLEN); break; default: snprintf(result->ip, sizeof(result->ip), "shouldneverhappen"); break; } // get the mac address if (ioctl(sock, SIOCGIFHWADDR, &ifr[i]) < 0) { goto exit; } // turn it into a nice string snprintf(result->mac, sizeof(result->mac), "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned char) ifr[i].ifr_hwaddr.sa_data[0], (unsigned char) ifr[i].ifr_hwaddr.sa_data[1], (unsigned char) ifr[i].ifr_hwaddr.sa_data[2], (unsigned char) ifr[i].ifr_hwaddr.sa_data[3], (unsigned char) ifr[i].ifr_hwaddr.sa_data[4], (unsigned char) ifr[i].ifr_hwaddr.sa_data[5]); ret = true; break; // break out of the loop } exit: if (interfaces.ifc_buf != NULL) { free(interfaces.ifc_buf); } return ret; } bool nicutils_infofordefaultroute(nicinfo* result) { char* defaultroute = route_defaultrouteint(); if (defaultroute == NULL) { return false; } return nicutils_info(defaultroute, result); }