2009-10-08

Obtaining all IP of all interfaces on GNU/Linux using rtnetlink

Generated to HTML using c2html

/* Author: Pedro Francisco
* License: GPLv2 or any later version
*
* Code between "#ifdef ASKDJSD" and "#endif" is
* subject to another license and is here
* for informative motives only
*/



#include <stdio.h>
#include <stdlib.h>

#include <sys/ioctl.h>
#include <string.h>

#include <assert.h>


#include <net/if.h>
#include <linux/rtnetlink.h>

#include <netdb.h> /* for NI_MAXHOST and friends */

#include <arpa/inet.h> /* for inet_ntop() */

/* Structure listing at the bottom */


int main(void)
{
int fd; /* socket descriptor */
struct sockaddr_nl sa;


struct request {
struct nlmsghdr nh;
struct ifaddrmsg ifa;
} req;

struct reply {
struct nlmsghdr nh;
struct ifaddrmsg ifa;
struct rtattr rta;
/* 1) there should be buffer
* 2) and another rtattr
* 3) and loop ending with a buffer
*/

};

struct nlmsghdr *nh;
struct rtattr* ra; /* both to help parse the reply */
int len, nllen, ralen; /* we keep separate lengths for macros NLMSG_NEXT and RTA_NEXT to work without disturbing the other */
int i, parseNr; /* simple counters */
int endFlag; /* for the while loop */

char hbuf[NI_MAXHOST]; /* where we're gonna save the address */


int bufferSize=4096; /* default receive buffer */
void* recvd3=malloc(bufferSize);



memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;

fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);


/* req filling */
memset(&req, 0, sizeof(req));

req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; /* man 7 netlink. we must ask for a complete dump, since NLM_F_MATCH
* hasn't been implemented. therefore, parse is done on userspace.
*/

req.nh.nlmsg_type = RTM_GETADDR; /* man 7 rtnetlink */

req.nh.nlmsg_len = sizeof(req); /* we should've used NLMSG_LENGTH(struct ifinfomsg ifi). it works anyway though*/

send(fd, &req, req.nh.nlmsg_len, 0);
/* we sent the request packet */



/* receiving reply */
parseNr=0;
endFlag=0;

do { /* While not the end of multipart message. May be sent into a 2nd recv call */

memset(recvd3, 0, bufferSize);
len=recv(fd, recvd3, bufferSize, 0);

if (len==bufferSize) { /* We may have not received the whole message */
fprintf(stderr, "WARNING: message probably truncated. Increase 'bufferSize'\n");
fprintf(stderr, " proceeding anyway\n");
}
if (len < 0) {
perror("ERROR: reception of netlink packet failed: ");
return -1;
}

fprintf(stderr, "DEBUG: length of received packet: %d\n", len);


fprintf(stderr, "DEBUG: parse number: %d\n", parseNr++);

nllen=len;
for (nh = &(((struct reply*)(recvd3))->nh); NLMSG_OK (nh, nllen); nh = NLMSG_NEXT (nh, nllen)) {
fprintf(stderr, "DEBUG: started new NLMSGHDR.\n");

/* The end of multipart message. */
if (nh->nlmsg_type == NLMSG_DONE) {
fprintf(stderr, "DEBUG: this NLMSG is the last message.\n");
endFlag=1; /* stop condition set */
}

/* Multipart message. */
if (nh->nlmsg_flags == NLM_F_MULTI)
fprintf(stderr, "DEBUG: this NLMSG is part of a multi-message.\n");

/* Do some error handling. */
if (nh->nlmsg_type == NLMSG_ERROR) {
fprintf(stderr, "ERROR: NLMSG has error");
struct nlmsgerr* tmp=NLMSG_DATA(nh);
fprintf(stderr, " Error code: %d.\n", tmp->error);
fprintf(stderr, " Length of message including header: %d.\n", tmp->msg.nlmsg_len);
fprintf(stderr, " Type of message content: %d.\n", tmp->msg.nlmsg_type);
fprintf(stderr, " Additional flags: %d.\n", tmp->msg.nlmsg_flags);
fprintf(stderr, " Sequence number: %d.\n", tmp->msg.nlmsg_seq);
fprintf(stderr, " PID of the sending process: %d.\n", tmp->msg.nlmsg_pid);
return -2;
}

if (nh->nlmsg_type == NLMSG_NOOP)
continue;

printf("DEBUG: interface ID: %d\n", ((struct reply* )(nh))->ifa.ifa_index); /* Thanks 'Santa Claus' for making me see I
* was screwing up here ;)


/* Continue with parsing payload. */

ralen=nllen;
for (ra = &(((struct reply*)(nh))->rta); RTA_OK (ra, ralen); ra = RTA_NEXT (ra, ralen)) {
fprintf(stderr, "DEBUG: started new RTATTR.\n");

switch((ra->rta_type)) {
case IFA_UNSPEC : printf("DEBUG: type is IFA_UNSPEC\n"); break;
case IFA_ADDRESS : printf("DEBUG: type is IFA_ADDRESS\n"); break;
case IFA_LOCAL : printf("DEBUG: type is IFA_LOCAL\n"); break;
case IFA_LABEL : printf("DEBUG: type is IFA_LABEL\n"); break;
case IFA_BROADCAST : printf("DEBUG: type is IFA_BROADCAST\n"); break;
case IFA_ANYCAST : printf("DEBUG: type is IFA_ANYCAST\n"); break;
case IFA_CACHEINFO : printf("DEBUG: type is IFA_CACHEINFO\n"); break;
}


if (ra->rta_type == IFA_LABEL) {
printf("\tvalue: ", ra->rta_type);
for(i=0; i < ra->rta_len; i++)
printf("%c", *((char*)RTA_DATA(ra)+i));
printf("\n");
} else if (ra->rta_type == IFA_ADDRESS || ra->rta_type == IFA_LOCAL) {

if((inet_ntop((((struct reply* )(nh))->ifa.ifa_family), \
RTA_DATA(ra), hbuf, NI_MAXHOST)) != NULL) /* Thanks iproute and its author
* Alexey Kuznetsov, was at a loss here
*/

printf("\tvalue: %s\n", hbuf);

strncpy(hbuf, ">EMPTY<", NI_MAXHOST);
} else
printf("\tIGNORED.\n");



}
}

} while (endFlag==0);

free(recvd3);

printf("DEBUG: normal termination.\n");

return 0;

}



#ifdef ASKDJSD
struct msghdr {
void * msg_name; /* Socket name */
int msg_namelen; /* Length of name */
struct iovec * msg_iov; /* Data blocks */
__kernel_size_t msg_iovlen; /* Number of blocks */
void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
__kernel_size_t msg_controllen; /* Length of cmsg list */
unsigned msg_flags;
};

struct sockaddr_nl
{
sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
};

struct nlmsghdr {
__u32 nlmsg_len; /* Length of message including header. */
__u16 nlmsg_type; /* Type of message content. */
__u16 nlmsg_flags; /* Additional flags. */
__u32 nlmsg_seq; /* Sequence number. */
__u32 nlmsg_pid; /* PID of the sending process. */
};

struct nlmsgerr {
int error; /* Negative errno or 0 for acknowledgements */
struct nlmsghdr msg; /* Message header that caused the error */
};

struct ifinfomsg {
unsigned char ifi_family; /* AF_UNSPEC */
unsigned short ifi_type; /* Device type */
int ifi_index; /* Interface index */
unsigned int ifi_flags; /* Device flags */
unsigned int ifi_change; /* change mask */
};

struct rtattr {
unsigned short rta_len; /* Length of option */
unsigned short rta_type; /* Type of option */
/* Data follows */
};

struct ifaddrmsg {
unsigned char ifa_family; /* Address type */
unsigned char ifa_prefixlen; /* Prefixlength of address */
unsigned char ifa_flags; /* Address flags */
unsigned char ifa_scope; /* Address scope */
int ifa_index; /* Interface index */
};



#endif


Generated to HTML using c2html

9 comments:

zómaigode said...

Save us leet hax!

Zeca said...

666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666

lovers and loners said...

posts com código? :|

Halloween said...

Ou então fazes ifconfig.... e grepas o output :D

Saurnil said...

alguem se importa de dizer o que é que essas linhas todas fazem? Só percebi o
#include iostream
#include stdlib.h

Pedro Francisco said...

Abrem um canal de comunicação com o kernel do tipo AF_NETLINK, subtipo RTNETLINK, ou seja, tudo o que é responsável pela rede no kernel.

Fazem um pedido ácerca de uma secção específica e percorrem-na usando macros especialmente feitas para o efeito (NLMSG_NEXT & RTA_NEXT) em busca da informação sobre a interface que queremos. Neste caso imprimo sempre que encontro algo que está num formato que me interessa, ou seja, IP e nome de interface.

Bruno Aleixo said...

A mim que me importa!

Peres said...

ASKDJSD é o que? head on keyboard? Há nomes melhor para isso.

E, embora seja progamação ingénua, é funcional.

Para o teu nível é claro.

Claro.

Saurnil said...

ah... assim bem explicadinho até percebi... not.