|
23 | 23 | #include <arpa/inet.h> |
24 | 24 | #include <sstream> |
25 | 25 | #include <sys/socket.h> |
| 26 | +#include <netinet/ether.h> |
26 | 27 |
|
27 | 28 | #include "../exceptions.h" |
28 | 29 |
|
29 | 30 | #ifndef SOL_NETLINK |
30 | 31 | #define SOL_NETLINK 270 |
31 | 32 | #endif |
32 | 33 |
|
| 34 | +/*useful for memory paging*/ |
| 35 | +#define SIZE_ALIGN 8192 |
| 36 | +#define NLMSG_TAIL(nmsg) \ |
| 37 | + ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) |
| 38 | + |
| 39 | +struct nl_req { |
| 40 | + struct nlmsghdr nlmsg; |
| 41 | + struct ifinfomsg ifinfomsg; |
| 42 | +}; |
| 43 | + |
| 44 | +struct nl_ipreq { |
| 45 | + struct nlmsghdr nlmsg; |
| 46 | + struct ifaddrmsg ifaddrmsg; |
| 47 | +}; |
| 48 | + |
33 | 49 | namespace polycube { |
34 | 50 | namespace polycubed { |
35 | 51 |
|
@@ -616,5 +632,220 @@ void Netlink::detach_from_xdp(const std::string &iface, int attach_flags) { |
616 | 632 | logger->debug("XDP program detached from port: {0}", iface); |
617 | 633 | } |
618 | 634 |
|
| 635 | +struct nlmsghdr* Netlink::netlink_alloc() { |
| 636 | + size_t len = NLMSG_ALIGN(SIZE_ALIGN) + NLMSG_ALIGN(sizeof(struct nlmsghdr *)); |
| 637 | + struct nlmsghdr *nlmsg = (struct nlmsghdr *) malloc(len); |
| 638 | + memset(nlmsg, 0, len); |
| 639 | + |
| 640 | + struct nl_req *unr = (struct nl_req *)nlmsg; |
| 641 | + unr->ifinfomsg.ifi_family = AF_UNSPEC; |
| 642 | + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); |
| 643 | + nlmsg->nlmsg_type = RTM_NEWLINK; |
| 644 | + // NLM_F_REQUEST Must be set on all request messages |
| 645 | + // NLM_F_ACK Request for an acknowledgment on success |
| 646 | + nlmsg->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; |
| 647 | + |
| 648 | + return nlmsg; |
| 649 | +} |
| 650 | + |
| 651 | +struct nlmsghdr* Netlink::netlink_ip_alloc() { |
| 652 | + size_t len = NLMSG_ALIGN(SIZE_ALIGN) + NLMSG_ALIGN(sizeof(struct nlmsghdr *)); |
| 653 | + struct nlmsghdr *nlmsg = (struct nlmsghdr *) malloc(len); |
| 654 | + memset(nlmsg, 0, len); |
| 655 | + |
| 656 | + struct nl_ipreq *uni = (struct nl_ipreq *)nlmsg; |
| 657 | + uni->ifaddrmsg.ifa_family = AF_INET; |
| 658 | + uni->ifaddrmsg.ifa_scope = 0; |
| 659 | + |
| 660 | + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); |
| 661 | + nlmsg->nlmsg_type = RTM_NEWADDR; |
| 662 | + // NLM_F_REQUEST Must be set on all request messages |
| 663 | + // NLM_F_ACK Request for an acknowledgment on success |
| 664 | + // NLM_F_CREATE Create object if it doesn't already exist |
| 665 | + // NLM_F_EXCL Don't replace if the object already exists |
| 666 | + nlmsg->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_CREATE|NLM_F_EXCL; |
| 667 | + |
| 668 | + return nlmsg; |
| 669 | +} |
| 670 | + |
| 671 | +int Netlink::netlink_nl_send(struct nlmsghdr *nlmsg) { |
| 672 | + struct sockaddr_nl nladdr; |
| 673 | + struct iovec iov = { |
| 674 | + .iov_base = (void*)nlmsg, |
| 675 | + .iov_len = nlmsg->nlmsg_len, |
| 676 | + }; |
| 677 | + struct msghdr msg = { |
| 678 | + .msg_name = &nladdr, |
| 679 | + .msg_namelen = sizeof(nladdr), |
| 680 | + .msg_iov = &iov, |
| 681 | + .msg_iovlen = 1, |
| 682 | + }; |
| 683 | + int nlfd; |
| 684 | + |
| 685 | + memset(&nladdr, 0, sizeof(struct sockaddr_nl)); |
| 686 | + nladdr.nl_family = AF_NETLINK; |
| 687 | + nladdr.nl_pid = 0; |
| 688 | + nladdr.nl_groups = 0; |
| 689 | + |
| 690 | + nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
| 691 | + if (nlfd < 0) { |
| 692 | + free(nlmsg); |
| 693 | + logger->error("netlink_nl_send: Unable to open socket"); |
| 694 | + throw std::runtime_error("netlink_nl_send: Unable to open socket"); |
| 695 | + } |
| 696 | + |
| 697 | + if (sendmsg(nlfd, &msg, 0) < 0) { |
| 698 | + free(nlmsg); |
| 699 | + close(nlfd); |
| 700 | + logger->error("netlink_nl_send: Unable to sendmsg"); |
| 701 | + throw std::runtime_error("netlink_nl_send: Unable to sendmsg"); |
| 702 | + } |
| 703 | + |
| 704 | + /* read the reply message to check that there are no errors */ |
| 705 | + if (recvmsg(nlfd, &msg, 0) < 0) { |
| 706 | + free(nlmsg); |
| 707 | + close(nlfd); |
| 708 | + logger->error("netlink_nl_send: Unable to recvmsg"); |
| 709 | + throw std::runtime_error("netlink_nl_send: Unable to recvmsg"); |
| 710 | + } |
| 711 | + |
| 712 | + /* if there is an error return error code */ |
| 713 | + if (nlmsg->nlmsg_type == NLMSG_ERROR) { |
| 714 | + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(nlmsg); |
| 715 | + free(nlmsg); |
| 716 | + close(nlfd); |
| 717 | + return err->error; |
| 718 | + } |
| 719 | + |
| 720 | + free(nlmsg); |
| 721 | + close(nlfd); |
| 722 | + return 0; |
| 723 | +} |
| 724 | + |
| 725 | +void Netlink::set_iface_status(const std::string &iface, IFACE_STATUS status) { |
| 726 | + struct nlmsghdr *nlmsg = netlink_alloc(); |
| 727 | + struct nl_req *unr = (struct nl_req *)nlmsg; |
| 728 | + |
| 729 | + int index = get_iface_index(iface); |
| 730 | + if (index == -1) { |
| 731 | + logger->error("set_iface_status: iface {0} does not exist", iface); |
| 732 | + throw std::runtime_error("set_iface_status: iface does not exist"); |
| 733 | + } |
| 734 | + |
| 735 | + unr->ifinfomsg.ifi_index = index; |
| 736 | + unr->ifinfomsg.ifi_change |= IFF_UP; |
| 737 | + if (status == IFACE_STATUS::UP) |
| 738 | + unr->ifinfomsg.ifi_flags |= IFF_UP; |
| 739 | + else if (status == IFACE_STATUS::DOWN) |
| 740 | + unr->ifinfomsg.ifi_flags |= ~IFF_UP; |
| 741 | + |
| 742 | + netlink_nl_send(nlmsg); |
| 743 | +} |
| 744 | + |
| 745 | +void Netlink::set_iface_mac(const std::string &iface, const std::string &mac) { |
| 746 | + struct rtnl_link *link; |
| 747 | + struct rtnl_link *old_link; |
| 748 | + struct nl_sock *sk; |
| 749 | + struct nl_cache *link_cache; |
| 750 | + |
| 751 | + sk = nl_socket_alloc(); |
| 752 | + if (nl_connect(sk, NETLINK_ROUTE) < 0) { |
| 753 | + logger->error("set_iface_mac: Unable to open socket"); |
| 754 | + throw std::runtime_error("set_iface_mac: Unable to open socket"); |
| 755 | + } |
| 756 | + |
| 757 | + link = rtnl_link_alloc(); |
| 758 | + if (!link) { |
| 759 | + nl_close(sk); |
| 760 | + logger->error("set_iface_mac: Invalid link"); |
| 761 | + throw std::runtime_error("set_iface_mac: Invalid link"); |
| 762 | + } |
| 763 | + |
| 764 | + rtnl_link_set_name(link, iface.c_str()); |
| 765 | + |
| 766 | + struct nl_addr* addr; |
| 767 | + addr = nl_addr_build(AF_LLC, ether_aton(mac.c_str()), ETH_ALEN); |
| 768 | + if (!addr) { |
| 769 | + nl_close(sk); |
| 770 | + logger->error("set_iface_mac: Invalid MAC address"); |
| 771 | + throw std::runtime_error("set_iface_mac: Invalid MAC address"); |
| 772 | + } |
| 773 | + rtnl_link_set_addr(link, addr); |
| 774 | + |
| 775 | + if (rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache) < 0) { |
| 776 | + nl_close(sk); |
| 777 | + logger->error("set_iface_mac: Unable to allocate cache"); |
| 778 | + throw std::runtime_error("set_iface_mac: Unable to allocate cache"); |
| 779 | + } |
| 780 | + |
| 781 | + old_link = rtnl_link_get_by_name(link_cache, iface.c_str()); |
| 782 | + if (rtnl_link_change(sk, old_link, link, 0) < 0) { |
| 783 | + nl_close(sk); |
| 784 | + logger->error("set_iface_mac: Unable to change link"); |
| 785 | + throw std::runtime_error("set_iface_mac: Unable to change link"); |
| 786 | + } |
| 787 | + |
| 788 | + rtnl_link_put(link); |
| 789 | + nl_close(sk); |
| 790 | +} |
| 791 | + |
| 792 | +void Netlink::set_iface_ip(const std::string &iface, const std::string &ip, int prefix) { |
| 793 | + struct nlmsghdr *nlmsg = netlink_ip_alloc(); |
| 794 | + struct nl_ipreq *uni = (struct nl_ipreq *)nlmsg; |
| 795 | + struct rtattr *rta; |
| 796 | + struct in_addr ia; |
| 797 | + |
| 798 | + int index = get_iface_index(iface); |
| 799 | + if (index == -1) { |
| 800 | + logger->error("set_iface_ip: iface {0} does not exist", iface); |
| 801 | + throw std::runtime_error("set_iface_ip: iface does not exist"); |
| 802 | + } |
| 803 | + |
| 804 | + uni->ifaddrmsg.ifa_index = index; |
| 805 | + uni->ifaddrmsg.ifa_prefixlen = prefix; |
| 806 | + |
| 807 | + if (inet_pton(AF_INET, ip.c_str(), &ia) <= 0) { |
| 808 | + free(nlmsg); |
| 809 | + logger->error("set_iface_ip: Error in inet_pton"); |
| 810 | + throw std::runtime_error("set_iface_ip: Error in inet_pton"); |
| 811 | + } |
| 812 | + |
| 813 | + rta = NLMSG_TAIL(nlmsg); |
| 814 | + rta->rta_type = IFA_LOCAL; |
| 815 | + rta->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
| 816 | + memcpy(RTA_DATA(rta), &ia, sizeof(struct in_addr)); |
| 817 | + nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + RTA_ALIGN(rta->rta_len); |
| 818 | + |
| 819 | + rta = NLMSG_TAIL(nlmsg); |
| 820 | + rta->rta_type = IFA_ADDRESS; |
| 821 | + rta->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
| 822 | + memcpy(RTA_DATA(rta), &ia, sizeof(struct in_addr)); |
| 823 | + nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + RTA_ALIGN(rta->rta_len); |
| 824 | + |
| 825 | + netlink_nl_send(nlmsg); |
| 826 | +} |
| 827 | + |
| 828 | +void Netlink::move_iface_into_ns(const std::string &iface, int fd) { |
| 829 | + struct nlmsghdr *nlmsg = netlink_alloc(); |
| 830 | + struct nl_req *unr = (struct nl_req *)nlmsg; |
| 831 | + struct rtattr *rta; |
| 832 | + |
| 833 | + int index = get_iface_index(iface); |
| 834 | + if (index == -1) { |
| 835 | + logger->error("move_iface_into_ns: iface {0} does not exist", iface); |
| 836 | + throw std::runtime_error("move_iface_into_ns: iface does not exist"); |
| 837 | + } |
| 838 | + |
| 839 | + unr->ifinfomsg.ifi_index = index; |
| 840 | + rta = NLMSG_TAIL(nlmsg); |
| 841 | + rta->rta_type = IFLA_NET_NS_FD; |
| 842 | + rta->rta_len = RTA_LENGTH(sizeof(int)); |
| 843 | + |
| 844 | + memcpy(RTA_DATA(rta), &fd, sizeof(pid_t)); |
| 845 | + nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + RTA_ALIGN(rta->rta_len); |
| 846 | + |
| 847 | + netlink_nl_send(nlmsg); |
| 848 | +} |
| 849 | + |
619 | 850 | } // namespace polycubed |
620 | 851 | } // namespace polycube |
0 commit comments