2010-07-20 15 views
15

He mirado alrededor como loco, pero no obtengo una respuesta real. Tengo un ejemplo, pero eso dependía de la propia biblioteca de las personas, por lo que no era muy buena.Obteniendo la puerta de enlace para usar para una IP determinada en ANSI C

Al principio quería obtener la puerta de enlace predeterminada de una interfaz, pero como las diferentes direcciones IP podían enrutarse de manera diferente, comprendí rápidamente que lo que quería obtener la puerta de enlace para una IP de destino determinada utilizando un socket AF_ROUTE y rtm_type RTM_GET. ¿Alguien tiene un ejemplo donde realmente termino con una cadena que contiene las puertas de enlace IP (o dirección MAC)? La entrada de la puerta de enlace parece estar en hexadecimal, pero también está codificada en/proc/net/route, donde supongo que el socket AF_ROUTE obtiene su información de (pero a través del kernel, supongo).

Gracias por adelantado

y p.s. Acabo de empezar a usar el desbordamiento de pila y debo decir que ¡todos ustedes son geniales! Respuestas rápidas y buenas! Eres mis nuevos mejores amigos;)

Respuesta

16

Esto es específico del sistema operativo, no hay una API unificada (o ANSI C) para esto.

Asumiendo Linux, la mejor manera es simplemente analizar/proc/net/route, busque la entrada donde Destino es 00000000, la puerta de enlace predeterminada está en la columna Gateway, donde puede leer la representación hexadecimal de la puerta de enlace IP abordar (en big endian, creo)

Si desea hacerlo a través de llamadas a la API más específicas, tendrá que pasar por bastantes aros, aquí es un programa de ejemplo:

#include <netinet/in.h> 
#include <net/if.h> 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/socket.h> 
#include <sys/ioctl.h> 
#include <linux/netlink.h> 
#include <linux/rtnetlink.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 


#define BUFSIZE 8192 
char gateway[255]; 

struct route_info { 
    struct in_addr dstAddr; 
    struct in_addr srcAddr; 
    struct in_addr gateWay; 
    char ifName[IF_NAMESIZE]; 
}; 

int readNlSock(int sockFd, char *bufPtr, int seqNum, int pId) 
{ 
    struct nlmsghdr *nlHdr; 
    int readLen = 0, msgLen = 0; 

do { 
    /* Recieve response from the kernel */ 
     if ((readLen = recv(sockFd, bufPtr, BUFSIZE - msgLen, 0)) < 0) { 
      perror("SOCK READ: "); 
      return -1; 
     } 

     nlHdr = (struct nlmsghdr *) bufPtr; 

    /* Check if the header is valid */ 
     if ((NLMSG_OK(nlHdr, readLen) == 0) 
      || (nlHdr->nlmsg_type == NLMSG_ERROR)) { 
      perror("Error in recieved packet"); 
      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) { 
      /* return if its not */ 
      break; 
     } 
    } while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId)); 
    return msgLen; 
} 
/* For printing the routes. */ 
void printRoute(struct route_info *rtInfo) 
{ 
    char tempBuf[512]; 

/* Print Destination address */ 
    if (rtInfo->dstAddr.s_addr != 0) 
     strcpy(tempBuf, inet_ntoa(rtInfo->dstAddr)); 
    else 
     sprintf(tempBuf, "*.*.*.*\t"); 
    fprintf(stdout, "%s\t", tempBuf); 

/* Print Gateway address */ 
    if (rtInfo->gateWay.s_addr != 0) 
     strcpy(tempBuf, (char *) inet_ntoa(rtInfo->gateWay)); 
    else 
     sprintf(tempBuf, "*.*.*.*\t"); 
    fprintf(stdout, "%s\t", tempBuf); 

    /* Print Interface Name*/ 
    fprintf(stdout, "%s\t", rtInfo->ifName); 

    /* Print Source address */ 
    if (rtInfo->srcAddr.s_addr != 0) 
     strcpy(tempBuf, inet_ntoa(rtInfo->srcAddr)); 
    else 
     sprintf(tempBuf, "*.*.*.*\t"); 
    fprintf(stdout, "%s\n", tempBuf); 
} 

void printGateway() 
{ 
    printf("%s\n", gateway); 
} 
/* For parsing the route info returned */ 
void parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo) 
{ 
    struct rtmsg *rtMsg; 
    struct rtattr *rtAttr; 
    int rtLen; 

    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; 

/* get the rtattr field */ 
    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.s_addr= *(u_int *) RTA_DATA(rtAttr); 
      break; 
     case RTA_PREFSRC: 
      rtInfo->srcAddr.s_addr= *(u_int *) RTA_DATA(rtAttr); 
      break; 
     case RTA_DST: 
      rtInfo->dstAddr .s_addr= *(u_int *) RTA_DATA(rtAttr); 
      break; 
     } 
    } 
    //printf("%s\n", inet_ntoa(rtInfo->dstAddr)); 

    if (rtInfo->dstAddr.s_addr == 0) 
     sprintf(gateway, (char *) inet_ntoa(rtInfo->gateWay)); 
    //printRoute(rtInfo); 

    return; 
} 


int main() 
{ 
    struct nlmsghdr *nlMsg; 
    struct rtmsg *rtMsg; 
    struct route_info *rtInfo; 
    char msgBuf[BUFSIZE]; 

    int sock, len, msgSeq = 0; 

/* Create Socket */ 
    if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) 
     perror("Socket Creation: "); 

    memset(msgBuf, 0, BUFSIZE); 

/* point the header and the msg structure pointers into the buffer */ 
    nlMsg = (struct nlmsghdr *) msgBuf; 
    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 (send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0) { 
     printf("Write To Socket Failed...\n"); 
     return -1; 
    } 

/* Read the response */ 
    if ((len = readNlSock(sock, msgBuf, msgSeq, getpid())) < 0) { 
     printf("Read From Socket Failed...\n"); 
    return -1; 
    } 
/* Parse and print the response */ 
    rtInfo = (struct route_info *) malloc(sizeof(struct route_info)); 
//fprintf(stdout, "Destination\tGateway\tInterface\tSource\n"); 
    for (; NLMSG_OK(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len)) { 
     memset(rtInfo, 0, sizeof(struct route_info)); 
     parseRoutes(nlMsg, rtInfo); 
    } 
    free(rtInfo); 
    close(sock); 

    printGateway(); 
    return 0; 
} 
+0

Gran ejemplo de trabajo; Gracias. Si bien esto solo proporciona la puerta de enlace IPv4, también se puede modificar para encontrar la puerta de enlace IPv6 (si alguien tiene curiosidad por saber). – jdknight

2

decidí ir a la manera "rápida y sucia" para comenzar y leer el IP desde /proc/net/route usando netstat -rm.

Pensé que compartiría mi función ... Tenga en cuenta que hay un error en ella y quizás pueda ayudarme a encontrarla y la editaré sin fallas. La función toma un nombre iface como eth0 y devuelve la ip de la puerta de enlace utilizada por ese iface.

char* GetGatewayForInterface(const char* interface) { 
    char* gateway = NULL; 

    FILE* fp = popen("netstat -rn", "r"); 
    char line[256]={0x0}; 

    while(fgets(line, sizeof(line), fp) != NULL) 
    {  
    /* 
    * Get destination. 
    */ 
    char* destination; 
    destination = strndup(line, 15); 

    /* 
    * Extract iface to compare with the requested one 
    * todo: fix for iface names longer than eth0, eth1 etc 
    */ 
    char* iface; 
    iface = strndup(line + 73, 4); 


    // Find line with the gateway 
    if(strcmp("0.0.0.0  ", destination) == 0 && strcmp(iface, interface) == 0) { 
     // Extract gateway 
     gateway = strndup(line + 16, 15); 
    } 

    free(destination); 
    free(iface); 
    } 

    pclose(fp); 
    return gateway; 
} 

El problema con esta función es que cuando salgo de allí provoca una corrupción de la memoria. Pero funciona si elimino la llamada a pclose (pero esa no sería una buena solución porque la transmisión permanecería abierta ... jeje). Entonces, si alguien puede detectar el error, editaré la función con la versión correcta. No soy un gurú de C y me confundo un poco acerca de todo el manejo de la memoria;)

+0

No hay nada en el código que haya publicado que pueda causar un bloqueo de memoria-corrupción. Lo confirmé ejecutándolo con valgrind. – indiv

4

Quizás esta es una pregunta muy antigua pero tuve el mismo problema y no puedo encontrar un mejor resultado. Finalmente resolví mi problema con este código que tiene algunos cambios. Entonces decido compartirlo.

char* GetGatewayForInterface(const char* interface) 
{ 
    char* gateway = NULL; 

    char cmd [1000] = {0x0}; 
    sprintf(cmd,"route -n | grep %s | grep 'UG[ \t]' | awk '{print $2}'", interface); 
    FILE* fp = popen(cmd, "r"); 
    char line[256]={0x0}; 

    if(fgets(line, sizeof(line), fp) != NULL) 
     gateway = string(line); 


    pclose(fp); 
} 
Cuestiones relacionadas