summaryrefslogtreecommitdiff
path: root/nicutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'nicutils.c')
-rw-r--r--nicutils.c258
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);
+}