int usnic_nl_rt_lookup(uint32_t src_addr, uint32_t dst_addr, int oif, uint32_t *nh_addr) { struct usnic_nl_sk *unlsk; struct nl_msg *nlm; struct rtmsg rmsg; struct usnic_rt_cb_arg arg; int err; retry: unlsk = NULL; err = usnic_nl_sk_alloc(&unlsk, NETLINK_ROUTE); if (err) return err; memset(&rmsg, 0, sizeof(rmsg)); rmsg.rtm_family = AF_INET; rmsg.rtm_dst_len = sizeof(dst_addr) * CHAR_BIT; rmsg.rtm_src_len = sizeof(src_addr) * CHAR_BIT; nlm = nlmsg_alloc_simple(RTM_GETROUTE, 0); if (!nlm) { usnic_err("Failed to alloc nl message, %s\n", NL_GETERROR(err)); err = ENOMEM; goto out; } nlmsg_append(nlm, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO); nla_put_u32(nlm, RTA_DST, dst_addr); nla_put_u32(nlm, RTA_SRC, src_addr); err = usnic_nl_send_query(unlsk, nlm, NETLINK_ROUTE, NLM_F_REQUEST); nlmsg_free(nlm); if (err < 0) { usnic_err("Failed to send RTM_GETROUTE query message, error %s\n", NL_GETERROR(err)); err = EINVAL; goto out; } memset(&arg, 0, sizeof(arg)); arg.oif = oif; arg.unlsk = unlsk; err = nl_socket_modify_cb(unlsk->nlh, NL_CB_MSG_IN, NL_CB_CUSTOM, usnic_rt_raw_parse_cb, &arg); if (err != 0) { usnic_err("Failed to setup callback function, error %s\n", NL_GETERROR(err)); err = EINVAL; goto out; } /* Sometimes the recvmsg can fail because something is * temporarily out of resources. In this case, delay a little * and try again. */ do { err = 0; NL_RECVMSGS(unlsk->nlh, arg, EAGAIN, err, out); if (err == EAGAIN) { usleep(5); } } while (err == EAGAIN); /* If we got a reply back that indicated that the kernel was * too busy to handle this request, delay a little and try * again. */ if (arg.retry) { usleep(5); goto retry; } if (arg.found) { *nh_addr = arg.nh_addr; err = 0; } else { err = EHOSTUNREACH; } out: usnic_nl_sk_free(unlsk); return err; }
int usnic_nl_rt_lookup(uint32_t src_addr, uint32_t dst_addr, int oif, uint32_t *nh_addr) { struct usnic_nl_sk *unlsk; struct nl_msg *nlm; struct rtmsg rmsg; struct usnic_rt_cb_arg arg; int err; unlsk = NULL; err = usnic_nl_sk_alloc(&unlsk, NETLINK_ROUTE); if (err) return err; memset(&rmsg, 0, sizeof(rmsg)); rmsg.rtm_family = AF_INET; rmsg.rtm_dst_len = sizeof(dst_addr) * CHAR_BIT; rmsg.rtm_src_len = sizeof(src_addr) * CHAR_BIT; nlm = nlmsg_alloc_simple(RTM_GETROUTE, 0); if (!nlm) { usnic_err("Failed to alloc nl message, %s\n", NL_GETERROR(err)); err = ENOMEM; goto out; } nlmsg_append(nlm, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO); nla_put_u32(nlm, RTA_DST, dst_addr); nla_put_u32(nlm, RTA_SRC, src_addr); err = usnic_nl_send_query(unlsk, nlm, NETLINK_ROUTE, NLM_F_REQUEST); nlmsg_free(nlm); if (err < 0) { usnic_err("Failed to send RTM_GETROUTE query message, error %s\n", NL_GETERROR(err)); err = EINVAL; goto out; } memset(&arg, 0, sizeof(arg)); arg.oif = oif; arg.unlsk = unlsk; err = nl_socket_modify_cb(unlsk->nlh, NL_CB_MSG_IN, NL_CB_CUSTOM, usnic_rt_raw_parse_cb, &arg); if (err != 0) { usnic_err("Failed to setup callback function, error %s\n", NL_GETERROR(err)); err = EINVAL; goto out; } NL_RECVMSGS(unlsk->nlh, arg, EHOSTUNREACH, err, out); if (arg.found) { *nh_addr = arg.nh_addr; err = 0; } else { err = EHOSTUNREACH; } out: usnic_nl_sk_free(unlsk); return err; }