diff options
Diffstat (limited to 'nicutils.c')
-rw-r--r-- | nicutils.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/nicutils.c b/nicutils.c new file mode 100644 index 0000000..4396858 --- /dev/null +++ b/nicutils.c @@ -0,0 +1,258 @@ +/* + * nicutils.c + * + * Created on: 16 Aug 2012 + * Author: daniel + */ + +#include "nicutils.h" + +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <netinet/if_ether.h> +#include <netdb.h> +#include <netinet/in.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <netinet/ether.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#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); +} |