Linux下閘道器地址的獲取
阿新 • • 發佈:2019-01-29
使用者空間獲取閘道器地址
有兩種方法,一個是從/proc/net/route中讀取,這是最簡單,最直接的,route命令就是這麼做的,可以參考net-tools包中route的原始碼實現。
另一種是用Netlink來實現。利用NETLINK_ROUTE(rtnetlink.c: Routing netlink socket interface)的RTM_GETROUTE指令查詢路由,這是從網上找的程式碼,在Debian (2.6.26核心)下測試通過。
核心空間獲取閘道器地址#include <arpa/inet.h> //for in_addr #include <linux/rtnetlink.h> //for rtnetlink #include <net/if.h> //for IF_NAMESIZ, route_info #include <stdlib.h> //for malloc(), free() #include <string.h> //for strstr(), memset() #include <string> #define BUFSIZE 8192 struct route_info{ u_int dstAddr; u_int srcAddr; u_int gateWay; char ifName[IF_NAMESIZE]; }; int readNlSock(int sockFd, char *bufPtr, int seqNum, int pId) { struct nlmsghdr *nlHdr; int readLen = 0, msgLen = 0; do{ //收到核心的應答 if((readLen = recv(sockFd, bufPtr, BUFSIZE - msgLen, 0)) < 0) { perror("SOCK READ: "); return -1; } nlHdr = (struct nlmsghdr *)bufPtr; //檢查header是否有效 if((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR)) { perror("Error in recieved packet"); return -1; } if(nlHdr->nlmsg_type == NLMSG_DONE) { break; } else { bufPtr += readLen; msgLen += readLen; } if((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) { break; } } while((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId)); return msgLen; } //分析返回的路由資訊 void parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo,char *gateway) { struct rtmsg *rtMsg; struct rtattr *rtAttr; int rtLen; char *tempBuf = NULL; struct in_addr dst; struct in_addr gate; tempBuf = (char *)malloc(100); 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; rtAttr = (struct rtattr *)RTM_RTA(rtMsg); rtLen = RTM_PAYLOAD(nlHdr); for(;RTA_OK(rtAttr,rtLen);rtAttr = RTA_NEXT(rtAttr,rtLen)){ switch(rtAttr->rta_type) { case RTA_OIF: if_indextoname(*(int *)RTA_DATA(rtAttr), rtInfo->ifName); break; case RTA_GATEWAY: rtInfo->gateWay = *(u_int *)RTA_DATA(rtAttr); break; case RTA_PREFSRC: rtInfo->srcAddr = *(u_int *)RTA_DATA(rtAttr); break; case RTA_DST: rtInfo->dstAddr = *(u_int *)RTA_DATA(rtAttr); break; } } dst.s_addr = rtInfo->dstAddr; if (strstr((char *)inet_ntoa(dst), "0.0.0.0")) { printf("oif:%s",rtInfo->ifName); gate.s_addr = rtInfo->gateWay; sprintf(gateway, (char *)inet_ntoa(gate)); printf("%s\n",gateway); gate.s_addr = rtInfo->srcAddr; printf("src:%s\n",(char *)inet_ntoa(gate)); gate.s_addr = rtInfo->dstAddr; printf("dst:%s\n",(char *)inet_ntoa(gate)); } free(tempBuf); return; } int get_gateway(char *gateway) { struct nlmsghdr *nlMsg; struct rtmsg *rtMsg; struct route_info *rtInfo; char msgBuf[BUFSIZE]; int sock, len, msgSeq = 0; if((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { perror("Socket Creation: "); return -1; } memset(msgBuf, 0, BUFSIZE); nlMsg = (struct nlmsghdr *)msgBuf; rtMsg = (struct rtmsg *)NLMSG_DATA(nlMsg); 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. if(send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0){ printf("Write To Socket Failed…\n"); return -1; } if((len = readNlSock(sock, msgBuf, msgSeq, getpid())) < 0) { printf("Read From Socket Failed…\n"); return -1; } rtInfo = (struct route_info *)malloc(sizeof(struct route_info)); for(;NLMSG_OK(nlMsg,len);nlMsg = NLMSG_NEXT(nlMsg,len)){ memset(rtInfo, 0, sizeof(struct route_info)); parseRoutes(nlMsg, rtInfo,gateway); } free(rtInfo); close(sock); return 0; } int main() { char buff[256]; get_gateway(buff); return 0; }
使用者空間的實現,其本質上是核心空間的支援,因此核心空間獲取應該更直接點。我參考了NETLINK_ROUTE中的實現來做,即執行一個從本機IP到外網IP的路由查詢,獲得的路由記錄中自然包括閘道器地址,主要用到ip_route_output_key()函式。下面是我的程式碼:
暫時只找到這種實現方式,有新的發現再來更新:)… extern struct net init_net; … inline void printIP(__u32 uip) { printk(NIPQUAD_FMT,NIPQUAD(uip)); } … int xxxxxx() { … int err; struct rtable * rt = NULL; struct flowi fl = { .nl_u = { .ip4_u = { .daddr = 0, .saddr = 0, .tos = 0, }, }, .oif = 0, }; fl.nl_u.ip4_u.daddr = in_aton("182.168.1.1"); fl.nl_u.ip4_u.saddr = in_aton("192.168.0.186"); err = ip_route_output_key(&init_net, &rt, &fl); if(rt) { if(rt->idev&&rt->idev->dev&&rt->idev->dev->name) printk(" if:%s\n",rt->idev->dev->name); printk(" gw:"); printIP(rt->rt_gateway); printk("\n dst:"); printIP(rt->rt_dst); printk("\n src:"); printIP(rt->rt_src); printk("\n"); } else printk("rt = NULL!\n"); … }