#ifdef HAVE_CONFIG_H # include #endif #include #include #include /* memcpy */ #include #include #include #include #include #include #include /* ntohl htonl */ #include "route_tool.h" static struct glob { unsigned seq; unsigned dev; unsigned long gateway; unsigned long dst; #ifdef USE_IPV6 unsigned char gateway6[16]; unsigned char dst6[16]; #endif int *pstatus; } glob = { 0, }; #if 0 /* AVM only needed for debugmsg */ static int dh = -1; #endif #define syserror(x) #define errmsg(x) #define debugmsg(x) /* Utility function for parse rtattr. */ static void parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, int len) { while (RTA_OK (rta, len)) { if (rta->rta_type <= max) tb[rta->rta_type] = rta; rta = RTA_NEXT (rta, len); } } static int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) { int len = RTA_LENGTH(alen); struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) return -1; rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; memcpy(RTA_DATA(rta), data, alen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; return 0; } static int handle_getroute(struct nlmsghdr *h, int error_code) { // struct routegetinfo rt, *rp = 0; // struct rtm_sentget *p, **pp; struct rtattr *tb[RTA_MAX + 1]; struct rtmsg *r; // char *dev = 0; int len; if (getpid() != h->nlmsg_pid || glob.seq != h->nlmsg_seq) return -1; if (error_code < 0) { debugmsg((dh, "iproute get: ok")); } else { *glob.pstatus = error_code; return 0; } len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); if (len < 0) return -1; r = NLMSG_DATA (h); memset(tb, 0, sizeof(tb)); parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); if (tb[RTA_OIF]) { // dev = index2ifname(*((int *)RTA_DATA(tb[RTA_OIF]))); glob.dev = *((int *)RTA_DATA(tb[RTA_OIF])); } else { glob.dev = (unsigned)-1; } if (r->rtm_family == AF_INET) { if (tb[RTA_GATEWAY]) { glob.gateway = ntohl(*((__u32 *)RTA_DATA(tb[RTA_GATEWAY]))); } else { glob.gateway = 0; } if (tb[RTA_DST]) { glob.dst = ntohl(*((__u32 *)RTA_DATA(tb[RTA_DST]))); } else { glob.dst = 0; } } #ifdef USE_IPV6 else if (r->rtm_family == AF_INET6) { if (tb[RTA_GATEWAY]) { memcpy(glob.gateway6, RTA_DATA(tb[RTA_GATEWAY]), sizeof(glob.gateway6)); } else { memset(glob.gateway6, 0, sizeof(glob.gateway6)); } if (tb[RTA_DST]) { memcpy(glob.dst6, RTA_DATA(tb[RTA_DST]), sizeof(glob.dst6)); } else { memset(glob.dst6, 0, sizeof(glob.dst6)); } } #endif *glob.pstatus = 0; return 0; } static int check_for_newroute(struct sockaddr_nl *snl, struct nlmsghdr *h) { struct rtmsg *r; int len; debugmsg((dh, "check_for_newroute")); if (h->nlmsg_type != RTM_NEWROUTE) return -1; len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); if (len < 0) return -1; r = NLMSG_DATA (h); if (r->rtm_family != AF_INET && r->rtm_family != AF_INET6) return -1; if (handle_getroute(h, -1) == 0) { return 0; } return 0; } static void netlink_read(int fd) { char buf[4096]; struct iovec iov = { buf, sizeof buf }; struct sockaddr_nl snl; struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 }; struct nlmsghdr *h; int count = 5; int status; debugmsg((dh, "netlink_read start")); next_message: if (--count <= 0) return; while ((status = recvmsg(fd, &msg, 0)) <= 0) { if (status < 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK || errno == EAGAIN) return; syserror(("recvmsg")); return; } if (status == 0) { errmsg(("%s: recvmsg EOF")); close(fd); // TODO return; } } debugmsg((dh, "netlink_read: recvmsg ok")); if (msg.msg_namelen != sizeof(snl)) { errmsg(("sender address length error: length %d", msg.msg_namelen)); goto next_message; } for (h = (struct nlmsghdr *) buf; NLMSG_OK(h, status); h = NLMSG_NEXT(h, status)) { debugmsg((dh, "netlink: type %d len %d flags 0x%x seq %d pid %d", h->nlmsg_type, h->nlmsg_len, h->nlmsg_flags, h->nlmsg_seq, h->nlmsg_pid)); /* * Finish of reading. */ if (h->nlmsg_type == NLMSG_DONE) { debugmsg((dh, "netlink_read: done")); goto next_message; } /* * Error handling. */ if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(h); if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { errmsg(("error: message truncated")); goto next_message; } if ( /* forget_request(&err->msg, -err->error) < 0 && */ handle_getroute(h, -err->error) < 0) { if (err->error) { errno = -err->error; syserror(("netlink: no request found for error")); } else { errmsg(("netlink: no request found for ack")); } } /* * If the error field is zero, then this is an ACK */ if (err->error == 0) { /* * return if not a multipart message, otherwise continue */ if (!(h->nlmsg_flags & NLM_F_MULTI)) { debugmsg((dh, "netlink_read: done (not multi)")); goto next_message; } continue; } goto next_message; } if (check_for_newroute(&snl, h) == 0) continue; } /* * After error care. */ if (msg.msg_flags & MSG_TRUNC) { errmsg(("error: message truncated")); } else if (status) { errmsg(("error: data remain size %d", status)); } goto next_message; } static int _route_tool_get_dev(int family, unsigned char *addr, unsigned *pdev) { struct { struct nlmsghdr n; struct rtmsg r; char buf[1024]; } req; struct sockaddr_nl snl; int fd, ret; fd = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { syserror(("netlink_init: socket() failed")); return -1; } ret = fcntl (fd, F_SETFL, O_NONBLOCK); if (ret < 0) { syserror(("netlink_init: fcntl(O_NONBLOCK) failed")); close(fd); return -1; } memset (&snl, 0, sizeof snl); snl.nl_family = AF_NETLINK; if (AF_INET == family) { snl.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_ROUTE|RTMGRP_IPV4_IFADDR; } else { snl.nl_groups = RTMGRP_LINK|RTMGRP_IPV6_ROUTE|RTMGRP_IPV6_IFADDR; } ret = bind (fd, (struct sockaddr *) &snl, sizeof(snl)); if (ret < 0) { syserror(("netlink_init: bind() failed")); close(fd); return -1; } memset(&req, 0, sizeof(req)); 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 = family; if (AF_INET == family) { req.r.rtm_dst_len = 4*8; addattr_l(&req.n, sizeof(req), RTA_DST, addr, 4); } #ifdef USE_IPV6 else if (AF_INET6 == family) { req.r.rtm_dst_len = 16*8; addattr_l(&req.n, sizeof(req), RTA_DST, addr, 16); } #endif req.n.nlmsg_pid = getpid(); req.n.nlmsg_seq = ++glob.seq; memset (&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; if (sendto (fd, (void *) &req, req.n.nlmsg_len, 0, (struct sockaddr *) &snl, sizeof(snl)) < 0) { syserror(("netlink: sendto: failed")); close(fd); return -1; } int status = -99; glob.pstatus = &status; do { netlink_read(fd); } while(status == -99); close(fd); if (status > 0) return -1; if (glob.dev == (unsigned)-1) return -1; *pdev = glob.dev; return 0; } #ifdef USE_IPV6 int route_tool_get_ipv6_dev(unsigned char *ipv6addr /* 16 byte */, unsigned *pdev, unsigned char *gateway6, unsigned char *dst6) { int ret = _route_tool_get_dev(AF_INET6, ipv6addr, pdev); if (0 == ret) { memcpy(gateway6, glob.gateway6, 16); memcpy(dst6, glob.dst6, 16); } return ret; } #endif int route_tool_get_dev(unsigned long ipaddr/*hostorder*/, unsigned *pdev, unsigned long *pgateway, unsigned long *pdst) { __u32 addr = htonl(ipaddr); int ret = _route_tool_get_dev(AF_INET, (unsigned char *)&addr, pdev); if (ret == 0) { *pgateway = glob.gateway; *pdst = glob.dst; } return ret; }