在linux下查询路由

在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;
}

你可能感兴趣的:(在linux下查询路由)