在linux查询路由的方法有很多种。下面介绍一种从iproute开源软件中提取出来的一种方法。在ubuntu904下编译,运行通过。
编译: gcc -o reqroute iproute_linux.c
使用: ./reqroute 1.2.3.4
结果:source:1.3.4.5
Gateway:192.168.1.3
Dev:eth0
源代码如下:
iproute_linux.h
typedef unsigned char __u8;
typedef short __s16;
typedef unsigned int __u32;
typedef struct{
__u8 family;
__u8 bytelen;
__s16 bitlen;
__u32 flags;
__u32 data[8];
} inet_prefix;
static struct{
int tb;
int cloned;
int flushed;
char *flushb;
int flushp;
int flushe;
struct rtnl_handle *rth;
int protocol, protocolmask;
int scope, scopemask;
int type, typemask;
int tos, tosmask;
int iif, iifmask;
int oif, oifmask;
int realm, realmmask;
inet_prefix rprefsrc;
inet_prefix rvia;
inet_prefix rdst;
inet_prefix mdst;
inet_prefix rsrc;
inet_prefix msrc;
} filter;
struct dn_naddr{
unsigned short a_len;
unsigned char a_addr[20];
};
struct ipx_addr {
u_int32_t ipx_net;
u_int8_t ipx_node[6];
};
struct idxmap{
struct idxmap * next;
unsigned index;
int type;
int alen;
unsigned flags;
unsigned char addr[20];
char name[16];
};
struct rtnl_handle{
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
__u32 seq;
__u32 dump;
};
另一个文件:
iproute_linux.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "iproute.h"
static struct idxmap *idxmap[16];
static int flush_update(void){
if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
perror("Failed to send flush request/n");
return -1;
}
filter.flushp = 0;
return 0;
}
static int get_addr_1(inet_prefix *addr, const char *name, int family){
memset(addr, 0, sizeof(*addr));
if (strcmp(name, "default") == 0 ||
strcmp(name, "all") == 0 ||
strcmp(name, "any") == 0) {
addr->family = family;
addr->bytelen = (family == AF_INET6 ? 16 : 4);
addr->bitlen = -1;
return 0;
}
if (strchr(name, ':')) {
addr->family = AF_INET6;
if (family != AF_UNSPEC && family != AF_INET6){
return -1;
}
if (inet_pton(AF_INET6, name, addr->data) <= 0){
return -1;
}
addr->bytelen = 16;
addr->bitlen = -1;
return 0;
}
addr->family = AF_INET;
if (family != AF_UNSPEC && family != AF_INET){
return -1;
}
if (get_addr_ipv4((__u8 *)addr->data, name) <= 0){
return -1;
}
addr->bytelen = 4;
addr->bitlen = -1;
return 0;
}
static char *rtnl_rtntype_n2a(int id, char *buf, int len){
switch (id) {
case RTN_UNSPEC:
return "none";
case RTN_UNICAST:
return "unicast";
case RTN_LOCAL:
return "local";
case RTN_BROADCAST:
return "broadcast";
case RTN_ANYCAST:
return "anycast";
case RTN_MULTICAST:
return "multicast";
case RTN_BLACKHOLE:
return "blackhole";
case RTN_UNREACHABLE:
return "unreachable";
case RTN_PROHIBIT:
return "prohibit";
case RTN_THROW:
return "throw";
case RTN_NAT:
return "nat";
case RTN_XRESOLVE:
return "xresolve";
default:
snprintf(buf, len, "%d", id);
return buf;
}
}
static __inline__ int do_digit(char *str, u_int32_t addr, u_int32_t scale, size_t *pos, size_t len){
u_int32_t tmp = addr >> (scale * 4);
if (*pos == len)
return 1;
tmp &= 0x0f;
if (tmp > 9)
*str = tmp + 'A' - 10;
else
*str = tmp + '0';
(*pos)++;
return 0;
}
static const char *ipx_ntop1(const struct ipx_addr *addr, char *str, size_t len){
int i;
size_t pos = 0;
if (len == 0)
return str;
for(i = 7; i >= 0; i--)
if (do_digit(str + pos, ntohl(addr->ipx_net), i, &pos, len))
return str;
if (pos == len)
return str;
*(str + pos) = '.';
pos++;
for(i = 0; i < 6; i++) {
if (do_digit(str + pos, addr->ipx_node[i], 1, &pos, len))
return str;
if (do_digit(str + pos, addr->ipx_node[i], 0, &pos, len))
return str;
}
if (pos == len)
return str;
*(str + pos) = 0;
return str;
}
static const char *ipx_ntop(int af, const void *addr, char *str, size_t len){
switch(af) {
case AF_IPX:
errno = 0;
return ipx_ntop1((struct ipx_addr *)addr, str, len);
default:
errno = EAFNOSUPPORT;
}
return NULL;
}
static __inline__ int do_digit1(char *str, u_int16_t *addr, u_int16_t scale, size_t *pos, size_t len, int *started){
u_int16_t tmp = *addr / scale;
if (*pos == len){
return 1;
}
if (((tmp) > 0) || *started || (scale == 1)) {
*str = tmp + '0';
*started = 1;
(*pos)++;
*addr -= (tmp * scale);
}
return 0;
}
static __inline__ u_int16_t dn_ntohs(u_int16_t addr){
union {
u_int8_t byte[2];
u_int16_t word;
} u;
u.word = addr;
return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8);
}
static const char *dnet_ntop1(const struct dn_naddr *dna, char *str, size_t len){
u_int16_t addr = dn_ntohs(*(u_int16_t *)dna->a_addr);
u_int16_t area = addr >> 10;
size_t pos = 0;
int started = 0;
if (dna->a_len != 2){
return NULL;
}
addr &= 0x03ff;
if (len == 0){
return str;
}
if (do_digit1(str + pos, &area, 10, &pos, len, &started)){
return str;
}
if (do_digit1(str + pos, &area, 1, &pos, len, &started)){
return str;
}
if (pos == len){
return str;
}
*(str + pos) = '.';
pos++;
started = 0;
if (do_digit1(str + pos, &addr, 1000, &pos, len, &started)){
return str;
}
if (do_digit1(str + pos, &addr, 100, &pos, len, &started)){
return str;
}
if (do_digit1(str + pos, &addr, 10, &pos, len, &started)){
return str;
}
if (do_digit1(str + pos, &addr, 1, &pos, len, &started)){
return str;
}
if (pos == len){
return str;
}
*(str + pos) = 0;
return str;
}
static const char *dnet_ntop(int af, const void *addr, char *str, size_t len){
switch(af) {
case AF_DECnet:
errno = 0;
return dnet_ntop1((struct dn_naddr *)addr, str, len);
default:
errno = EAFNOSUPPORT;
}
return NULL;
}
static const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen){
switch (af) {
case AF_INET:
case AF_INET6:
return inet_ntop(af, addr, buf, buflen);
default:
return "";
}
}
static const char *format_host(int af, int len, const void *addr,char *buf, int buflen){
return rt_addr_n2a(af, len, addr, buf, buflen);
}
static int ll_remember_index(const struct sockaddr_nl *who,struct nlmsghdr *n, void *arg){
int h;
struct ifinfomsg *ifi = NLMSG_DATA(n);
struct idxmap *im, **imp;
struct rtattr *tb[IFLA_MAX+1];
if (n->nlmsg_type != RTM_NEWLINK)
return 0;
if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
return -1;
memset(tb, 0, sizeof(tb));
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
if (tb[IFLA_IFNAME] == NULL)
return 0;
h = ifi->ifi_index&0xF;
for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next)
if (im->index == ifi->ifi_index)
break;
if (im == NULL) {
im = malloc(sizeof(*im));
if (im == NULL)
return 0;
im->next = *imp;
im->index = ifi->ifi_index;
*imp = im;
}
im->type = ifi->ifi_type;
im->flags = ifi->ifi_flags;
if (tb[IFLA_ADDRESS]) {
int alen;
im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
if (alen > sizeof(im->addr))
alen = sizeof(im->addr);
memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
} else {
im->alen = 0;
memset(im->addr, 0, sizeof(im->addr));
}
strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
return 0;
}
static const char *ll_idx_n2a(unsigned idx, char *buf){
struct idxmap *im;
if (idx == 0){
return "*";
}
for (im = idxmap[idx&0xF]; im; im = im->next){
if (im->index == idx){
return im->name;
}
}
snprintf(buf, 16, "if%d", idx);
return buf;
}
static const char *ll_index_to_name(unsigned idx){
static char nbuf[16];
return ll_idx_n2a(idx, nbuf);
}
static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb){
__u32 table = r->rtm_table;
if (tb[RTA_TABLE]){
table = *(__u32*) RTA_DATA(tb[RTA_TABLE]);
}
return table;
}
static int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits){
const __u32 *a1 = a->data;
const __u32 *a2 = b->data;
int words = bits >> 0x05;
bits &= 0x1f;
if (words){
if (memcmp(a1, a2, words << 2)){
return -1;
}
}
if (bits) {
__u32 w1, w2;
__u32 mask;
w1 = a1[words];
w2 = a2[words];
mask = htonl((0xffffffff) << (0x20 - bits));
if ((w1 ^ w2) & mask){
return 1;
}
}
return 0;
}
#define SPRINT_BUF(x) char x[64]
int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg){
FILE *fp = (FILE*)arg;
struct rtmsg *r = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr * tb[RTA_MAX+1];
char abuf[256];
inet_prefix dst;
inet_prefix src;
inet_prefix prefsrc;
inet_prefix via;
int host_len = -1;
static int ip6_multiple_tables;
__u32 table;
char b1[64];
len -= NLMSG_LENGTH(sizeof(*r));
if (len < 0) {
fprintf(stderr, "BUG: wrong nlmsg len %d/n", len);
return -1;
}
if (r->rtm_family == AF_INET6){
host_len = 128;
}else if (r->rtm_family == AF_INET){
host_len = 32;
}
parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
table = rtm_get_table(r, tb);
if (r->rtm_family == AF_INET6 && table != RT_TABLE_MAIN){
ip6_multiple_tables = 1;
}
if (r->rtm_family == AF_INET6 && !ip6_multiple_tables) {
if (filter.cloned) {
if (!(r->rtm_flags&RTM_F_CLONED)){
return 0;
}
}
if (filter.tb) {
if (!filter.cloned && r->rtm_flags&RTM_F_CLONED){
return 0;
}
if (filter.tb == RT_TABLE_LOCAL) {
if (r->rtm_type != RTN_LOCAL){
return 0;
}
} else if (filter.tb == RT_TABLE_MAIN) {
if (r->rtm_type == RTN_LOCAL){
return 0;
}
} else {
return 0;
}
}
} else {
if (filter.cloned) {
if (!(r->rtm_flags&RTM_F_CLONED))
return 0;
}
if (filter.tb > 0 && filter.tb != table){
return 0;
}
}
if ((filter.protocol^r->rtm_protocol)&filter.protocolmask)
return 0;
if ((filter.scope^r->rtm_scope)&filter.scopemask)
return 0;
if ((filter.type^r->rtm_type)&filter.typemask)
return 0;
if ((filter.tos^r->rtm_tos)&filter.tosmask)
return 0;
if (filter.rdst.family &&
(r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len))
return 0;
if (filter.mdst.family &&
(r->rtm_family != filter.mdst.family ||
(filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len)))
return 0;
if (filter.rsrc.family &&
(r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len))
return 0;
if (filter.msrc.family &&
(r->rtm_family != filter.msrc.family ||
(filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len)))
return 0;
if (filter.rvia.family && r->rtm_family != filter.rvia.family)
return 0;
if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family)
return 0;
memset(&dst, 0, sizeof(dst));
dst.family = r->rtm_family;
if (tb[RTA_DST]){
memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8);
}
if (filter.rsrc.family || filter.msrc.family) {
memset(&src, 0, sizeof(src));
src.family = r->rtm_family;
if (tb[RTA_SRC]){
memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8);
}
}
if (filter.rvia.bitlen>0) {
memset(&via, 0, sizeof(via));
via.family = r->rtm_family;
if (tb[RTA_GATEWAY]){
memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len/8);
}
}
if (filter.rprefsrc.bitlen>0) {
memset(&prefsrc, 0, sizeof(prefsrc));
prefsrc.family = r->rtm_family;
if (tb[RTA_PREFSRC]){
memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len/8);
}
}
if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)){
return 0;
}
if (filter.mdst.family && filter.mdst.bitlen >= 0 &&
inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)){
return 0;
}
if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)){
return 0;
}
if (filter.msrc.family && filter.msrc.bitlen >= 0 &&
inet_addr_match(&src, &filter.msrc, r->rtm_src_len))
return 0;
if (filter.rvia.family && inet_addr_match(&via, &filter.rvia, filter.rvia.bitlen))
return 0;
if (filter.rprefsrc.family && inet_addr_match(&prefsrc, &filter.rprefsrc, filter.rprefsrc.bitlen))
return 0;
if (filter.realmmask) {
__u32 realms = 0;
if (tb[RTA_FLOW])
realms = *(__u32*)RTA_DATA(tb[RTA_FLOW]);
if ((realms^filter.realm)&filter.realmmask)
return 0;
}
if (filter.iifmask) {
int iif = 0;
if (tb[RTA_IIF])
iif = *(int*)RTA_DATA(tb[RTA_IIF]);
if ((iif^filter.iif)&filter.iifmask)
return 0;
}
if (filter.oifmask) {
int oif = 0;
if (tb[RTA_OIF])
oif = *(int*)RTA_DATA(tb[RTA_OIF]);
if ((oif^filter.oif)&filter.oifmask)
return 0;
}
if (filter.flushb &&
r->rtm_family == AF_INET6 &&
r->rtm_dst_len == 0 &&
r->rtm_type == RTN_UNREACHABLE &&
tb[RTA_PRIORITY] &&
*(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1)
return 0;
if (filter.flushb) {
struct nlmsghdr *fn;
if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
if (flush_update())
return -1;
}
fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
memcpy(fn, n, n->nlmsg_len);
fn->nlmsg_type = RTM_DELROUTE;
fn->nlmsg_flags = NLM_F_REQUEST;
fn->nlmsg_seq = 100;//++rth.seq;
filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
filter.flushed++;
// if (show_stats < 2)
// return 0;
}
if (r->rtm_type != RTN_UNICAST && !filter.type)
fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
if (tb[RTA_DST]) {
if (r->rtm_dst_len != host_len) {
fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,RTA_PAYLOAD(tb[RTA_DST]), RTA_DATA(tb[RTA_DST]), abuf, sizeof(abuf)),r->rtm_dst_len);
} else {
fprintf(fp, "%s ", format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_DST]),
RTA_DATA(tb[RTA_DST]),
abuf, sizeof(abuf))
);
}
} else if (r->rtm_dst_len) {
fprintf(fp, "0/%d ", r->rtm_dst_len);
} else {
fprintf(fp, "default ");
}
if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
fprintf(fp, "via %s ",
format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_GATEWAY]),
RTA_DATA(tb[RTA_GATEWAY]),
abuf, sizeof(abuf)));
}
if (tb[RTA_OIF] && filter.oifmask != -1)
fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
/* Do not use format_host(). It is our local addr
and symbolic name will not be useful.
*/
fprintf(fp, " src %s ",
rt_addr_n2a(r->rtm_family,
RTA_PAYLOAD(tb[RTA_PREFSRC]),
RTA_DATA(tb[RTA_PREFSRC]),
abuf, sizeof(abuf)));
}
fprintf(fp, "/n");
fflush(fp);
return 0;
}
/* This uses a non-standard parsing (ie not inet_aton, or inet_pton)
* because of legacy choice to parse 10.8 as 10.8.0.0 not 10.0.0.8
*/
int get_addr_ipv4(__u8 *ap, const char *cp){
int i;
for (i = 0; i < 4; i++) {
unsigned long n;
char *endp;
n = strtoul(cp, &endp, 0);
if (n > 255)
return -1; /* bogus network value */
if (endp == cp) /* no digits */
return -1;
ap[i] = n;
if (*endp == '/0')
break;
if (i == 3 || *endp != '.')
return -1; /* extra characters */
cp = endp + 1;
}
return 1;
}
void iproute_reset_filter(){
memset(&filter, 0, sizeof(filter));
filter.mdst.bitlen = -1;
filter.msrc.bitlen = -1;
}
int rtnl_send(struct rtnl_handle *rth, const char *buf, int len){
return send(rth->fd, buf, len, 0);
}
static __inline__ u_int16_t dn_htons(u_int16_t addr){
union {
u_int8_t byte[2];
u_int16_t word;
} u;
u.word = addr;
return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8);
}
static int dnet_num(const char *src, u_int16_t * dst){
int rv = 0;
int tmp;
*dst = 0;
while ((tmp = *src++) != 0) {
tmp -= '0';
if ((tmp < 0) || (tmp > 9))
return rv;
rv++;
(*dst) *= 10;
(*dst) += tmp;
}
return rv;
}
static int dnet_pton1(const char *src, struct dn_naddr *dna){
u_int16_t area = 0;
u_int16_t node = 0;
int pos;
pos = dnet_num(src, &area);
if ((pos == 0) || (area > 63) || (*(src + pos) != '.'))
return 0;
pos = dnet_num(src + pos + 1, &node);
if ((pos == 0) || (node > 1023))
return 0;
dna->a_len = 2;
*(u_int16_t *)dna->a_addr = dn_htons((area << 10) | node);
return 1;
}
int dnet_pton(int af, const char *src, void *addr){
int err;
switch (af) {
case AF_DECnet:
errno = 0;
err = dnet_pton1(src, (struct dn_naddr *)addr);
break;
default:
errno = EAFNOSUPPORT;
err = -1;
}
return err;
}
int matches(const char *cmd, const char *pattern){
int len = strlen(cmd);
if (len > strlen(pattern))
return -1;
return memcmp(pattern, cmd, len);
}
int mask2bits(__u32 netmask){
unsigned bits = 0;
__u32 mask = ntohl(netmask);
__u32 host = ~mask;
/* a valid netmask must be 2^n - 1 */
if ((host & (host + 1)) != 0)
return -1;
for (; mask; mask <<= 1)
++bits;
return bits;
}
int get_unsigned(unsigned *val, const char *arg, int base){
unsigned long res;
char *ptr;
if (!arg || !*arg)
return -1;
res = strtoul(arg, &ptr, base);
if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
return -1;
*val = res;
return 0;
}
static int get_netmask(unsigned *val, const char *arg, int base){
inet_prefix addr;
if (!get_unsigned(val, arg, base))
return 0;
/* try coverting dotted quad to CIDR */
if (!get_addr_1(&addr, arg, AF_INET) && addr.family == AF_INET) {
int b = mask2bits(addr.data[0]);
if (b >= 0) {
*val = b;
return 0;
}
}
return -1;
}
int get_prefix_1(inet_prefix *dst, char *arg, int family){
int err;
unsigned plen;
char *slash;
memset(dst, 0, sizeof(*dst));
if (strcmp(arg, "default") == 0 ||
strcmp(arg, "any") == 0 ||
strcmp(arg, "all") == 0) {
if (family == AF_DECnet)
return -1;
dst->family = family;
dst->bytelen = 0;
dst->bitlen = 0;
return 0;
}
slash = strchr(arg, '/');
if (slash)
*slash = 0;
err = get_addr_1(dst, arg, family);
if (err == 0) {
switch(dst->family) {
case AF_INET6:
dst->bitlen = 128;
break;
case AF_DECnet:
dst->bitlen = 16;
break;
default:
case AF_INET:
dst->bitlen = 32;
}
if (slash) {
if (get_netmask(&plen, slash+1, 0)
|| plen > dst->bitlen) {
err = -1;
goto done;
}
dst->flags |= 1;
dst->bitlen = plen;
}
}
done:
if (slash)
*slash = '/';
return err;
}
int get_prefix(inet_prefix *dst, char *arg, int family){
if (family == AF_PACKET) {
fprintf(stderr, "Error: /"%s/" may be inet prefix, but it is not allowed in this context./n", arg);
exit(1);
}
if (get_prefix_1(dst, arg, family)) {
fprintf(stderr, "Error: an inet prefix is expected rather than /"%s/"./n", arg);
exit(1);
}
return 0;
}
#define NLMSG_TAIL(nmsg) /
((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,int alen){
int len = RTA_LENGTH(alen);
struct rtattr *rta;
if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d/n",maxlen);
return -1;
}
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), data, alen);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
return 0;
}
int rcvbuf = 1024 * 1024;
int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,int protocol){
socklen_t addr_len;
int sndbuf = 32768;
memset(rth, 0, sizeof(*rth));
rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
if (rth->fd < 0) {
perror("Cannot open netlink socket");
return -1;
}
if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
perror("SO_SNDBUF");
return -1;
}
if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
perror("SO_RCVBUF");
return -1;
}
memset(&rth->local, 0, sizeof(rth->local));
rth->local.nl_family = AF_NETLINK;
rth->local.nl_groups = subscriptions;
if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
perror("Cannot bind netlink socket");
return -1;
}
addr_len = sizeof(rth->local);
if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
perror("Cannot getsockname");
return -1;
}
if (addr_len != sizeof(rth->local)) {
fprintf(stderr, "Wrong address length %d/n", addr_len);
return -1;
}
if (rth->local.nl_family != AF_NETLINK) {
fprintf(stderr, "Wrong address family %d/n", rth->local.nl_family);
return -1;
}
rth->seq = time(NULL);
return 0;
}
int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions){
return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
}
int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len){
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) {
if (rta->rta_type <= max)
tb[rta->rta_type] = rta;
rta = RTA_NEXT(rta,len);
}
if (len)
fprintf(stderr, "!!!Deficit %d, rta_len=%d/n", len, rta->rta_len);
return 0;
}
static struct idxmap *idxmap[16];
typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,struct nlmsghdr *n, void *);
struct rtnl_dump_filter_arg
{
rtnl_filter_t filter;
void *arg1;
rtnl_filter_t junk;
void *arg2;
};
int rtnl_dump_filter_l(struct rtnl_handle *rth,const struct rtnl_dump_filter_arg *arg){
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char buf[16384];
iov.iov_base = buf;
while (1) {
int status;
const struct rtnl_dump_filter_arg *a;
iov.iov_len = sizeof(buf);
status = recvmsg(rth->fd, &msg, 0);
if (status < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
fprintf(stderr, "netlink receive error %s (%d)/n",
strerror(errno), errno);
return -1;
}
if (status == 0) {
fprintf(stderr, "EOF on netlink/n");
return -1;
}
for (a = arg; a->filter; a++) {
struct nlmsghdr *h = (struct nlmsghdr*)buf;
while (NLMSG_OK(h, status)) {
int err;
if (nladdr.nl_pid != 0 ||
h->nlmsg_pid != rth->local.nl_pid ||
h->nlmsg_seq != rth->dump) {
if (a->junk) {
err = a->junk(&nladdr, h,
a->arg2);
if (err < 0)
return err;
}
goto skip_it;
}
if (h->nlmsg_type == NLMSG_DONE)
return 0;
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
fprintf(stderr,
"ERROR truncated/n");
} else {
errno = -err->error;
perror("RTNETLINK answers");
}
return -1;
}
err = a->filter(&nladdr, h, a->arg1);
if (err < 0)
return err;
skip_it:
h = NLMSG_NEXT(h, status);
}
} while (0);
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Message truncated/n");
continue;
}
if (status) {
fprintf(stderr, "!!!Remnant of size %d/n", status);
exit(1);
}
}
}
int rtnl_dump_filter(struct rtnl_handle *rth,rtnl_filter_t filter, void *arg1,rtnl_filter_t junk,void *arg2){
const struct rtnl_dump_filter_arg a[2] = {
{ .filter = filter, .arg1 = arg1, .junk = junk, .arg2 = arg2 },
{ .filter = NULL, .arg1 = NULL, .junk = NULL, .arg2 = NULL }
};
return rtnl_dump_filter_l(rth, a);
}
int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type){
struct {
struct nlmsghdr nlh;
struct rtgenmsg g;
} req;
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = type;
req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
req.g.rtgen_family = family;
return send(rth->fd, (void*)&req, sizeof(req), 0);
}
int ll_init_map(struct rtnl_handle *rth){
if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
perror("Cannot send dump request");
exit(1);
}
if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) {
fprintf(stderr, "Dump terminated/n");
exit(1);
}
return 0;
}
unsigned ll_name_to_index(const char *name){
static char ncache[16];
static int icache;
struct idxmap *im;
int i;
if (name == NULL)
return 0;
if (icache && strcmp(name, ncache) == 0)
return icache;
for (i=0; i<16; i++) {
for (im = idxmap[i]; im; im = im->next) {
if (strcmp(im->name, name) == 0) {
icache = im->index;
strcpy(ncache, name);
return im->index;
}
}
}
return if_nametoindex(name);
}
int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data){
int len = RTA_LENGTH(4);
struct rtattr *rta;
if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded/n",maxlen);
return -1;
}
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), &data, 4);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
return 0;
}
int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,unsigned groups, struct nlmsghdr *answer,rtnl_filter_t junk,void *jarg){
int status;
unsigned seq;
struct nlmsghdr *h;
struct sockaddr_nl nladdr;
struct iovec iov = {
.iov_base = (void*) n,
.iov_len = n->nlmsg_len
};
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char buf[16384];
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = peer;
nladdr.nl_groups = groups;
n->nlmsg_seq = seq = ++rtnl->seq;
if (answer == NULL)
n->nlmsg_flags |= NLM_F_ACK;
status = sendmsg(rtnl->fd, &msg, 0);
if (status < 0) {
perror("Cannot talk to rtnetlink");
return -1;
}
memset(buf,0,sizeof(buf));
iov.iov_base = buf;
while (1) {
iov.iov_len = sizeof(buf);
status = recvmsg(rtnl->fd, &msg, 0);
if (status < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
fprintf(stderr, "netlink receive error %s (%d)/n",
strerror(errno), errno);
return -1;
}
if (status == 0) {
fprintf(stderr, "EOF on netlink/n");
return -1;
}
if (msg.msg_namelen != sizeof(nladdr)) {
fprintf(stderr, "sender address length == %d/n", msg.msg_namelen);
exit(1);
}
for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
int err;
int len = h->nlmsg_len;
int l = len - sizeof(*h);
if (l<0 || len>status) {
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Truncated message/n");
return -1;
}
fprintf(stderr, "!!!malformed message: len=%d/n", len);
exit(1);
}
if (nladdr.nl_pid != peer ||
h->nlmsg_pid != rtnl->local.nl_pid ||
h->nlmsg_seq != seq) {
if (junk) {
err = junk(&nladdr, h, jarg);
if (err < 0)
return err;
}
/* Don't forget to skip that message. */
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
continue;
}
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
if (l < sizeof(struct nlmsgerr)) {
fprintf(stderr, "ERROR truncated/n");
} else {
errno = -err->error;
if (errno == 0) {
if (answer)
memcpy(answer, h, h->nlmsg_len);
return 0;
}
perror("RTNETLINK answers");
}
return -1;
}
if (answer) {
memcpy(answer, h, h->nlmsg_len);
return 0;
}
fprintf(stderr, "Unexpected reply!!!/n");
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
}
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Message truncated/n");
continue;
}
if (status) {
fprintf(stderr, "!!!Remnant of size %d/n", status);
exit(1);
}
}
}
int iproute_get(int argc, char **argv){
struct rtnl_handle rth;
struct {
struct nlmsghdr n;
struct rtmsg r;
char buf[1024];
} req;
memset(&req, 0, sizeof(req));
iproute_reset_filter();
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST;
req.n.nlmsg_type = RTM_GETROUTE;
req.r.rtm_family = AF_INET;
req.r.rtm_table = 0;
req.r.rtm_protocol = 0;
req.r.rtm_scope = 0;
req.r.rtm_type = 0;
req.r.rtm_src_len = 0;
req.r.rtm_dst_len = 0;
req.r.rtm_tos = 0;
while (argc > 0) {
inet_prefix addr;
get_prefix(&addr, *argv, req.r.rtm_family);
if (req.r.rtm_family == AF_UNSPEC)
req.r.rtm_family = addr.family;
if (addr.bytelen)
addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
req.r.rtm_dst_len = addr.bitlen;
argc--; argv++;
}
if (req.r.rtm_dst_len == 0) {
fprintf(stderr, "need at least destination address/n");
exit(1);
}
if (rtnl_open(&rth, 0) < 0){
exit(1);
}
ll_init_map(&rth);
if (req.r.rtm_family == AF_UNSPEC)
req.r.rtm_family = AF_INET;
if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0){
exit(2);
}
if (print_route(NULL, &req.n, (void*)stdout) < 0) {
fprintf(stderr, "An error :-)/n");
exit(1);
}
exit(0);
}
int main(int argc, char **argv){
iproute_get(argc-1, argv+1);
return 0;
}