#include // ioctl #include // needed by linux/if.h //#include #include #include // '' #include #include // PATH_MAX #include // fprintf #include // memset #include // open #include // writev #include // close #include "error.h" #include "ops.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) /****************************************************************************** * netlink.h ******************************************************************************/ #ifndef HICN_NETLINK_H #define HICN_NETLINK_H #include #include // DEPRECATED|/* Socket */ // DEPRECATED|int _nl_get_socket(); // DEPRECATED|int _nl_send(int s, uint8_t * buffer, size_t len); // DEPRECATED|size_t _nl_receive(uint8_t * buffer, size_t len); // DEPRECATED| // DEPRECATED|/* Netlink packet format */ // DEPRECATED|int _nl_header(int request, uint8_t * buffer, size_t len, uint32_t // flags); DEPRECATED|int _nl_payload_rule(uint8_t table_id, uint8_t * buffer, // size_t len); DEPRECATED|int _nl_payload_link(uint32_t ifindex, uint8_t * // buffer, size_t len); DEPRECATED|int _nl_payload_route(uint8_t table_id, // uint8_t dst_len, uint8_t * buffer, size_t len); DEPRECATED| DEPRECATED|int // _nl_parse(uint8_t * buffer, size_t len); DEPRECATED|int _nl_parse_ret(uint8_t // * buffer, size_t len); DEPRECATED|int _nl_parse_link_ifid(uint8_t * buffer, // size_t len, uint32_t * interface_id); DEPRECATED|int // _nl_parse_link_ip_addr(uint8_t * buffer, size_t len, struct in6_addr * addr); /* Public interface */ /** * Get the interface ID of an interface by its name * * @return 32-bit interface identifier in case of success, or 0. * * @see if_nametoindex * */ uint32_t _nl_get_ifid(const char *ifname); /** * Retrieve the output interface corresponding to the specified IP address. * * @param [in] addr IP(v6) address in presentation form. * @param [out] Identifier of the corresponding output interface. * @return int 0 in case of success, -1 otherwise */ int _nl_get_output_ifid(const char *ip_address, uint8_t address_family, uint32_t *interface_id); /** * Retrieve the first IP address of an interface (identified by its id) which * has a netmask < 128. * * @param [in] s File descriptor of the netlink socket (deprecated). * @param [in] interface_id Identifier of the interface for which to retrieve * the IP address. * @param [out] addr IP(v6) address in binary form. * @return int 0 in case of success, -1 otherwise * * @see getifaddrs */ int _nl_get_ip_addr(uint32_t interface_id, uint8_t address_family, ip_address_t *ip_address); int _nl_set_ip_addr(uint32_t interface_id, ip_address_t *ip_address); int _nl_up_if(uint32_t interface_id); int _nl_add_in_route_table(const ip_address_t *prefix, const uint32_t interface_id, const uint8_t table_id); int _nl_add_in_route_table_s(const char *prefix, const uint32_t interface_id, const uint8_t table_id); int _nl_add_in_route_s(const char *prefix, const uint32_t interface_id); int _nl_add_out_route(const char *gateway, const uint8_t address_family, const uint8_t table_idi, int default_route); int _nl_del_out_route(const char *gateway, const uint8_t address_family, const uint8_t table_id); int _nl_del_lo_route(const ip_address_t *ip_address); int _nl_add_rule(const char *interface_name, const uint8_t address_family, const uint8_t table_id); int _nl_del_rule(const char *interface_name, const uint8_t address_family, const uint8_t table_id); int _nl_add_neigh_proxy(const ip_address_t *ip_address, const uint32_t interface_id); int _nl_add_prio_rule(const ip_address_t *ip_address, const uint8_t address_family, const uint32_t priority, const uint8_t table_id); int _nl_add_lo_prio_rule(const ip_address_t *ip_address, const uint8_t address_family, const uint32_t priority); int _nl_del_prio_rule(const ip_address_t *ip_address, const uint8_t address_family, const uint32_t priority, const uint8_t table_id); int _nl_del_lo_prio_rule(const ip_address_t *ip_address, const uint8_t address_family, const uint32_t priority); #endif /* HICN_NETLINK_H */ /****************************************************************************** * netlink.c ******************************************************************************/ /* * This module offers an interface to the Netlink API appropriate for * implementing punting as required by hICN (1). * * More specifically, it consists of the following functionalities: * - LINK . map interface name to ID . set and interface up * - ADDR . get and set ip addresses on a given interface ID * - ROUTE . get output interface id towards IP (ip route get IP > interface_id) . add input route (ip route add PREFIX dev INTERFACE) for punting interests . add output route (ip route add default GATEWAY table TABLE) for routing interests (2, 3) . delete local route towards IP (ip route del IP table local) for ??? /!\ could this be avoided by removing the local attribute in the netlink call ? * - RULE * . add output rule (ip rule add iif interface table TABLE) for routing interests (2, 3) * - ND PROXY * . enable NDP proxy functionality for IP on interface ID (ip -6 neigh add proxy IP dev INTERFACE) * for allowing the TUN to be reachable on the reverse data path * * Implementation notes: * (1) We have not been using the libnl library because it requires * manipulating too many function and data structures for a simple purpose. * Currently, many parts of the code are somehow repetitive, but this might * be improved by a proper API in a future release. * (2) allows load balancing over different interfaces = multihoming. Please * note that it is not possible to have load balancing over two faces using * the same output interface as we are using the underlying IP network ! * This might be mitigated with the use of SR however. * (3) The implementation of punting heavily uses the policy routing * functionalities, as we need to hook through a TUN into user space a * whole prefix used as a destination (for interests) or source (for data * packets). We thus combine the use of rules to assign routing table IDs, * and routes inside those tables. As there is no easy way to allocate * which routing tables we use, we made the choice to index them by the ID * of the interface, assuming there is no external conflict. This might be * improved in the future. * * This hICN implementation uses TUNs in two different ways: * - a main TUN interface, which receives all punted interests, * demultiplex them before assigning them an input face (eventually * dynamically creating it); * - a set of output TUN interfaces, aka faces, used for routing of * interests, and for receiving the corresponding data packets on the way * back. Punting of data packets if based of their destination IP, which * is the IP of the physical output interface used for the interest, which * is unique (cf (2)). * * The corresponding routing tables IDs are : * MAIN_TUN_ID -> used for punting of data packets * OUTPUT_TUN_ID_i -> used for routing of interests towards next hop * (bypassing local IP routing table) * * Note that punting of interests is done just through a route, and routing * of data packets is done just through the regular IP routing table on the * note after the address translation done in the forwarder. * * - Forging netlink packets * * A previous implementation used function calls with pointers to populate * the various header parts in a buffer in order to build a netlink packet. * A newer implementation uses nested structs and iovecs to build the whole * packet in a single write call. This should allow a simpler evolution * towards a cleaner API. */ #include // inet_pton #include // errno #include // fib_rule_hdr, FRA_* #include #include #include // IFF_UP #include // in6addr #include // perror #include #include // '' #include // socket #include // read #include // '' #include // send, recv //#include "../../hicn.h" //#include "../../hicn_util.h" // ARRAY_SIZE, hicn_packet_dump_iov #define BUFSIZE 4096 #define FLAGS_CREATE NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK // ?? #define FLAGS_CREATE_MATCH \ NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_MATCH // XXX putting ACK poses a prolem for the value received by get_if_id. #define FLAGS_GET NLM_F_REQUEST #define FLAGS_GET_ROOT (NLM_F_REQUEST | NLM_F_ROOT) #define FLAGS_LIST NLM_F_REQUEST | NLM_F_DUMP #define IF_NAMESIZE 16 #define FR_ACT_TO_TBL 1 #define NLMSG_BOTTOM(nlmsg) \ ((struct rtattr *)(((void *)(nlmsg)) + NLMSG_ALIGN((nlmsg)->nlmsg_len))) int seq = 1; static inline size_t iov_length(const struct iovec *iov, unsigned long nr_segs) { unsigned long seg; size_t ret = 0; for (seg = 0; seg < nr_segs; seg++) ret += iov[seg].iov_len; return ret; } typedef struct { struct nlmsghdr hdr; struct nlmsgerr payload; } nl_err_hdr_t; /* Low level : nl header */ int _nl_get_socket() { return socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); } int _nl_header(int request, uint8_t *buffer, size_t len, uint32_t flags) { struct nlmsghdr *nl = (struct nlmsghdr *)buffer; nl->nlmsg_len = 0; // NLMSG_LENGTH(sizeof(struct ifinfomsg)); nl->nlmsg_type = request; nl->nlmsg_flags = flags; nl->nlmsg_seq = seq++; // nl->nlmsg_pid = 0; // getpid(); return 0; } /* Low level : nl protocols */ /* Low level : attributes */ int addAttr(struct nlmsghdr *nl, int maxlen, int type, void *data, int attr_len) { struct rtattr *rta; int len = RTA_LENGTH(attr_len); if (NLMSG_ALIGN(nl->nlmsg_len) + len > maxlen) { exit(EXIT_FAILURE); } rta = (struct rtattr *)((char *)nl + NLMSG_ALIGN(nl->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; memcpy(RTA_DATA(rta), data, attr_len); nl->nlmsg_len = NLMSG_ALIGN(nl->nlmsg_len) + len; return 0; } int _nl_payload_rule(uint8_t table_id, uint8_t address_family, uint8_t *buffer, size_t len) { struct nlmsghdr *nl = (struct nlmsghdr *)buffer; struct fib_rule_hdr *frh = (struct fib_rule_hdr *)(NLMSG_DATA(buffer)); memset(frh, 0, sizeof(struct fib_rule_hdr)); frh->family = address_family; frh->table = table_id; frh->action = FR_ACT_TO_TBL, frh->flags = NLM_F_REPLACE; // 0 frh->tos = 0; nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); return 0; } int _nl_payload_link(uint32_t ifindex, uint8_t *buffer, size_t len) { struct nlmsghdr *nl = (struct nlmsghdr *)buffer; struct ifinfomsg *ifi = (struct ifinfomsg *)(NLMSG_DATA(buffer)); memset(ifi, 0, sizeof(struct ifinfomsg)); ifi->ifi_family = AF_UNSPEC; // ifi->ifi_type = 0; ifi->ifi_index = ifindex; // new interface, could be specified since linux 3.7 ifi->ifi_flags = 0; // ifi->ifi_change = 0xffffffff; nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct ifinfomsg)); return 0; } int _nl_payload_addr(uint32_t ifindex, uint8_t *buffer, size_t len) { struct nlmsghdr *nl = (struct nlmsghdr *)buffer; struct ifaddrmsg *addr = (struct ifaddrmsg *)(NLMSG_DATA(buffer)); memset(addr, 0, sizeof(struct ifaddrmsg)); addr->ifa_family = AF_UNSPEC; // INET6; /* addr->ifa_prefixlen = 128; addr->ifa_flags = 0; addr->ifa_scope = RT_SCOPE_LINK; //IFA_ADDRESS; addr->ifa_index = ifindex; */ nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct ifaddrmsg)) - 4; return 0; } int _nl_payload_route(uint8_t table_id, uint8_t addr_family, uint8_t dst_len, uint8_t *buffer, size_t len) { struct nlmsghdr *nl = (struct nlmsghdr *)buffer; struct rtmsg *raddr = (struct rtmsg *)(NLMSG_DATA(buffer)); raddr->rtm_family = addr_family; raddr->rtm_dst_len = dst_len; raddr->rtm_src_len = 0; raddr->rtm_tos = 0; raddr->rtm_table = table_id; raddr->rtm_protocol = RTPROT_BOOT; raddr->rtm_scope = RT_SCOPE_UNIVERSE; raddr->rtm_type = RTN_UNICAST; raddr->rtm_flags = 0; nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct rtmsg)); return 0; } uint32_t _nl_get_ifid(const char *interface_name) { char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; size_t n; int fd; size_t len = interface_name ? strlen(interface_name) + 1 : 0; uint8_t padding[RTA_ALIGNTO] = {0, 0, 0, 0}; if (len == 0) { goto ERR_IF; } struct { struct nlmsghdr hdr; struct ifinfomsg payload; } msg = {//.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), .hdr.nlmsg_type = RTM_GETLINK, .hdr.nlmsg_flags = FLAGS_GET, .payload.ifi_family = AF_UNSPEC, .payload.ifi_index = 0}; struct rtattr a_ifname = {RTA_LENGTH(strlen(interface_name) + 1), IFLA_IFNAME}; struct iovec iov[] = {{&msg, sizeof(msg)}, {&a_ifname, sizeof(a_ifname)}, {(char *)interface_name, len}, {padding, RTA_SPACE(len) - RTA_LENGTH(len)}}; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR_SOCKET; } n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR_SEND; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR_RECV; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR_NL; } return 0; /* Unexpected */ } for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) { struct ifinfomsg *payload = (struct ifinfomsg *)NLMSG_DATA(hdr); return payload->ifi_index; } return 0; ERR_NL: ERR_RECV: ERR_SEND: ERR_SOCKET: ERR_IF: return 0; } int _nl_get_output_ifid(const char *ip_address, uint8_t family_address, uint32_t *interface_id) { int rc; char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; size_t n; int fd; fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR; } if (family_address == AF_INET6) { struct in6_addr addr; // V6SPECIFIC struct { struct nlmsghdr hdr; struct rtmsg payload; } msg = { .hdr.nlmsg_type = RTM_GETROUTE, .hdr.nlmsg_flags = NLM_F_REQUEST, .hdr.nlmsg_seq = seq++, .payload.rtm_family = AF_INET6, .payload.rtm_dst_len = IPV6_ADDR_LEN_BITS, .payload.rtm_src_len = 0, .payload.rtm_tos = 0, .payload.rtm_table = RT_TABLE_UNSPEC, .payload.rtm_protocol = RTPROT_UNSPEC, .payload.rtm_scope = RT_SCOPE_UNIVERSE, .payload.rtm_type = RTN_UNSPEC, .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get' }; /* Convert the IP address to binary form */ rc = inet_pton(AF_INET6, ip_address, &addr); if (rc <= 0) { goto ERR; } /* Set attribute = length/type/value */ struct rtattr a_dst = {RTA_LENGTH(16), RTA_DST}; struct iovec iov[] = { {&msg, sizeof(msg)}, {&a_dst, sizeof(a_dst)}, // attribute {&addr, sizeof(addr)} // value }; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR; } } else if (family_address == AF_INET) { struct in_addr addr; struct { struct nlmsghdr hdr; struct rtmsg payload; } msg = { .hdr.nlmsg_type = RTM_GETROUTE, .hdr.nlmsg_flags = NLM_F_REQUEST, .hdr.nlmsg_seq = seq++, .payload.rtm_family = AF_INET, .payload.rtm_dst_len = IPV4_ADDR_LEN_BITS, .payload.rtm_src_len = 0, .payload.rtm_tos = 0, .payload.rtm_table = RT_TABLE_UNSPEC, .payload.rtm_protocol = RTPROT_UNSPEC, .payload.rtm_scope = RT_SCOPE_UNIVERSE, .payload.rtm_type = RTN_UNSPEC, .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get' }; /* Convert the IP address to binary form */ rc = inet_pton(AF_INET, ip_address, &addr); if (rc <= 0) { goto ERR; } /* Set attribute = length/type/value */ struct rtattr a_dst = {RTA_LENGTH(4), RTA_DST}; struct iovec iov[] = { {&msg, sizeof(msg)}, {&a_dst, sizeof(a_dst)}, // attribute {&addr, sizeof(addr)} // value }; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR; } } else { goto ERR; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR; } return HICN_SOCKET_ERROR_UNEXPECTED; /* Unexpected */ } for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) { struct rtmsg *rtm = (struct rtmsg *)NLMSG_DATA(hdr); int attrlen = RTM_PAYLOAD(hdr); struct rtattr *rta; for (rta = RTM_RTA(rtm); RTA_OK(rta, attrlen); rta = RTA_NEXT(rta, attrlen)) { if (rta->rta_type == RTA_OIF) { *interface_id = *(uint32_t *)RTA_DATA(rta); return HICN_SOCKET_ERROR_NONE; } } } return HICN_SOCKET_ERROR_NONE; ERR: return HICN_SOCKET_ERROR_UNSPEC; } int _nl_get_ip_addr(uint32_t interface_id, uint8_t address_family, ip_address_t *ip_address) { char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; size_t n; int fd; struct { struct nlmsghdr hdr; struct ifaddrmsg payload; } msg = {.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), .hdr.nlmsg_type = RTM_GETADDR, .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT, // | NLM_F_MATCH, .payload.ifa_family = address_family, .payload.ifa_index = 0}; fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR_SOCKET; } n = send(fd, &msg, sizeof(msg), 0); if (n == -1) { goto ERR_SEND; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR_RECV; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR_NL; } return -99; /* Unexpected */ } for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) { struct ifaddrmsg *payload = (struct ifaddrmsg *)NLMSG_DATA(hdr); if (address_family == AF_INET6) { if ((payload->ifa_index == interface_id) && (payload->ifa_prefixlen < IPV6_ADDR_LEN * 8)) { printf("got ip address\n"); memcpy(ip_address->buffer, RTA_DATA(payload + 1), IPV6_ADDR_LEN); ip_address->family = AF_INET6; ip_address->prefix_len = IPV6_ADDR_LEN_BITS; printf("returning %d\n", HICN_SOCKET_ERROR_NONE); return HICN_SOCKET_ERROR_NONE; } } else if (address_family == AF_INET) { if ((payload->ifa_index == interface_id) && (payload->ifa_prefixlen < IPV4_ADDR_LEN * 8)) { printf("got ip address\n"); memcpy(ip_address->buffer, RTA_DATA(payload + 1), IPV4_ADDR_LEN); ip_address->family = AF_INET; ip_address->prefix_len = IPV4_ADDR_LEN_BITS; printf("returning %d\n", HICN_SOCKET_ERROR_NONE); return HICN_SOCKET_ERROR_NONE; } } else { return -99; } } ERR_NL: ERR_RECV: ERR_SEND: ERR_SOCKET: printf("error getting ip address\n"); return HICN_SOCKET_ERROR_UNSPEC; } int _nl_set_ip_addr(uint32_t interface_id, ip_address_t *ip_address) { char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; size_t n; int fd; struct { struct nlmsghdr hdr; struct ifaddrmsg payload; } msg = { .hdr.nlmsg_type = RTM_NEWADDR, .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC, .hdr.nlmsg_seq = seq++, .payload.ifa_family = ip_address->family, .payload.ifa_prefixlen = ip_address->prefix_len, .payload.ifa_flags = 0, .payload.ifa_scope = RT_SCOPE_UNIVERSE, .payload.ifa_index = interface_id}; /* Set attributes = length/type/value */ struct rtattr ifa_address = {RTA_LENGTH(ip_address_len(ip_address)), IFA_ADDRESS}; // XXX maybe the reason why we have a local route ? // struct rtattr ifa_local = { RTA_LENGTH(ip_address_len(ip_address)), // IFA_LOCAL }; struct iovec iov[] = { {&msg, sizeof(msg)}, {&ifa_address, sizeof(ifa_address)}, {(void *)&ip_address->buffer, sizeof(ip_address->buffer)}, // { &ifa_local, sizeof(ifa_local) }, // { (void*)&ip_address->buffer, sizeof(ip_address->buffer) }, }; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR_SOCKET; } // hicn_packet_dump_iov(iov, ARRAY_SIZE(iov)); n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR_SEND; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR_RECV; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR_NL; } } return 0; ERR_NL: ERR_RECV: ERR_SEND: ERR_SOCKET: return -1; } int _nl_up_if(uint32_t interface_id) { char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; size_t n; int fd; fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR_SOCKET; } struct { struct nlmsghdr hdr; struct ifinfomsg payload; } msg = { .hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), .hdr.nlmsg_type = RTM_NEWLINK, .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, .payload.ifi_family = AF_UNSPEC, .payload.ifi_index = interface_id, .payload.ifi_flags = IFF_UP, .payload.ifi_change = IFF_UP // 0xffffffff }; n = send(fd, &msg, sizeof(msg), 0); if (n == -1) { goto ERR_SEND; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR_RECV; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR_NL; } return 0; } ERR_NL: ERR_RECV: ERR_SEND: ERR_SOCKET: return -1; } struct route_info { char *dst_addr; char *src_addr; char *gateway; char ifName[IF_NAMESIZE]; }; /* * ip -6 route add PREFIX dev INTERFACE_NAME */ #if 0 int _nl_add_in_route(const char * prefix, const uint32_t interface_id) { char buffer[BUFSIZE]; struct nlmsghdr * hdr = (struct nlmsghdr *)buffer; size_t n; int fd; int pton_fd; unsigned char dst[sizeof(struct in6_addr)]; char * p; char * eptr; char addr[strlen(prefix)]; uint32_t dst_len; strncpy(addr, prefix, strlen(prefix)); p = strchr(addr, '/'); if (!p) { dst_len = IPV6_ADDR_LEN; } else { dst_len = strtoul(p + 1, &eptr, 10); if (dst_len > IPV6_ADDR_LEN * 8) { printf("E: Netmask > IPV6_ADDR_LEN"); return -1; } *p = 0; } pton_fd = inet_pton(AF_INET6, addr, dst); if (pton_fd <= 0) { if (pton_fd == 0) ;//ERROR("Not in presentation format"); else perror("inet_pton"); return -2; } _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE_MATCH); _nl_payload_route(RT_TABLE_MAIN, dst_len, (uint8_t *)buffer, BUFSIZE); addAttr(hdr, BUFSIZE, RTA_DST, dst, IPV6_ADDR_LEN); addAttr(hdr, BUFSIZE, RTA_OIF, (void*)&interface_id, sizeof(uint32_t)); fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR_SOCKET; } n = send(fd, buffer, hdr->nlmsg_len, 0); if (n == -1) { goto ERR_SEND; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR_RECV; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr * err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR_NL; } return 0; } ERR_NL: ERR_RECV: ERR_SEND: ERR_SOCKET: return -1; } #endif /* * ip -6 route add local default via GATEWAY_IP table TABLE_ID */ int _nl_add_out_route(const char *gateway, uint8_t address_family, const uint8_t table_id, int default_route) { char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; size_t n; int fd; int pton_fd; if (address_family == AF_INET) { struct in_addr gw; pton_fd = inet_pton(AF_INET, gateway, (struct in_addr *)&gw); if (pton_fd < 0) { return -1; } _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); /* gw */ addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); } else if (address_family == AF_INET6) { struct in6_addr gw; pton_fd = inet_pton(AF_INET6, gateway, (struct in6_addr *)&gw); if (pton_fd < 0) { return -1; } _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); /* gw */ addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); if (default_route != -1) { addAttr(hdr, BUFSIZE, RTA_OIF, &default_route, sizeof(default_route)); } } else { return -1; } // For more than 255 tables // addAttr(msg, BUFSIZE, RTA_TABLE, &table_id, sizeof(uint32_t)); fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR_SOCKET; } n = send(fd, buffer, hdr->nlmsg_len, 0); if (n == -1) { goto ERR_SEND; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR_RECV; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR_NL; } return 0; } ERR_NL: ERR_RECV: ERR_SEND: ERR_SOCKET: return -1; } /* * ip -6 route del local default via GATEWAY_IP table TABLE_ID */ int _nl_del_out_route(const char *gateway, const uint8_t address_family, const uint8_t table_id) { char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; size_t n; int fd; int pton_fd; if (address_family == AF_INET) { struct in_addr gw; pton_fd = inet_pton(AF_INET, gateway, (struct in_addr *)&gw); if (pton_fd < 0) { return -1; } _nl_header(RTM_DELROUTE, (uint8_t *)buffer, BUFSIZE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); /* gw */ addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); } else if (address_family == AF_INET6) { struct in6_addr gw; pton_fd = inet_pton(AF_INET6, gateway, (struct in6_addr *)&gw); if (pton_fd < 0) { return -1; } _nl_header(RTM_DELROUTE, (uint8_t *)buffer, BUFSIZE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); /* gw */ addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); } else { return -1; } // For more than 255 tables // addAttr(msg, BUFSIZE, RTA_TABLE, &table_id, sizeof(uint32_t)); fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR_SOCKET; } n = send(fd, buffer, hdr->nlmsg_len, 0); if (n == -1) { goto ERR_SEND; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR_RECV; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR_NL; } return 0; } ERR_NL: ERR_RECV: ERR_SEND: ERR_SOCKET: return -1; } /* * ip route del 1:2::2 dev lo table local * */ int _nl_del_lo_route(const ip_address_t *ip_address) { char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; size_t n; int fd; struct { struct nlmsghdr hdr; struct rtmsg payload; } msg = { .hdr.nlmsg_type = RTM_DELROUTE, .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, .hdr.nlmsg_seq = seq++, .payload.rtm_family = ip_address->family, .payload.rtm_dst_len = ip_address->prefix_len, .payload.rtm_src_len = 0, .payload.rtm_tos = 0, .payload.rtm_table = RT_TABLE_LOCAL, .payload.rtm_protocol = RTPROT_UNSPEC, .payload.rtm_scope = RT_SCOPE_UNIVERSE, .payload.rtm_type = RTN_UNSPEC, .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get' }; /* Set attribute = length/type/value */ uint32_t one = 1; struct rtattr a_dst = {RTA_LENGTH(ip_address_len(ip_address)), RTA_DST}; struct rtattr a_ifid_lo = {RTA_LENGTH(sizeof(uint32_t)), RTA_OIF}; struct iovec iov[] = { {&msg, sizeof(msg)}, /* Ip address */ {&a_dst, sizeof(a_dst)}, {(void *)&ip_address->buffer, ip_address_len(ip_address)}, /* Interface id */ {&a_ifid_lo, sizeof(a_ifid_lo)}, {&one, sizeof(one)}}; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR; } n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR; } return 0; } return HICN_SOCKET_ERROR_NONE; ERR: return HICN_SOCKET_ERROR_UNSPEC; } /* * ip -6 rule add iif INTERFACE_NAME lookup TABLE_ID */ int _nl_add_rule(const char *interface_name, uint8_t address_family, const uint8_t table_id) { char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; size_t n; int fd; _nl_header(RTM_NEWRULE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE); _nl_payload_rule(table_id, address_family, (uint8_t *)buffer, BUFSIZE); /* XXX iif */ addAttr(hdr, BUFSIZE, FRA_IIFNAME, (void *)interface_name, strlen(interface_name)); // attr1 = addNestedAttr(hdr, IFLA_LINKINFO); // endNestedAttr(hdr, attr1); fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR_SOCKET; } n = send(fd, buffer, hdr->nlmsg_len, 0); if (n == -1) { goto ERR_SEND; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR_RECV; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR_NL; } return 0; } ERR_NL: ERR_RECV: ERR_SEND: ERR_SOCKET: return -1; } /* * ip -6 rule del iif INTERFACE_NAME //lookup TABLE_ID */ int _nl_del_rule(const char *interface_name, uint8_t address_family, const uint8_t table_id) { char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; size_t n; int fd; _nl_header(RTM_DELRULE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE); _nl_payload_rule(table_id, address_family, (uint8_t *)buffer, BUFSIZE); /* XXX iif */ addAttr(hdr, BUFSIZE, FRA_IIFNAME, (void *)interface_name, strlen(interface_name)); fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR_SOCKET; } n = send(fd, buffer, hdr->nlmsg_len, 0); if (n == -1) { goto ERR_SEND; } n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR_RECV; } if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR_NL; } return 0; } ERR_NL: ERR_RECV: ERR_SEND: ERR_SOCKET: return -1; } /* * ip -6 neigh add proxy 1:2::2 dev hicnc-cons-eth0 2>&1 | grep nei * */ int _nl_add_neigh_proxy(const ip_address_t *ip_address, const uint32_t interface_id) { /* Buffer for holding the response, with appropriate casting on the header */ char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; /* Used for send and receive operations on netlink socket */ int fd; size_t n; /* Packet header */ struct { struct nlmsghdr hdr; struct ndmsg payload; } msg = { .hdr.nlmsg_type = RTM_NEWNEIGH, .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, .hdr.nlmsg_seq = seq++, .payload.ndm_family = ip_address->family, .payload.ndm_ifindex = interface_id, .payload.ndm_state = NUD_PERMANENT, .payload.ndm_flags = NTF_PROXY, }; /* Message attributes = length/type/value */ struct rtattr a_dst = {RTA_LENGTH(ip_address_len(ip_address)), NDA_DST}; /* Iovec describing the packets */ struct iovec iov[] = { {&msg, sizeof(msg)}, /* Ip address */ {&a_dst, sizeof(a_dst)}, {(void *)&ip_address->buffer, sizeof(ip_address->buffer)}, }; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); /* Open netlink socket */ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR; } /* Send packet */ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR; } /* Receive answer */ n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR; } /* Parse answer */ if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR; } } return HICN_SOCKET_ERROR_NONE; ERR: return HICN_SOCKET_ERROR_UNSPEC; } /* ip -6 route add 0:1::/64 dev hicn-if0 table 100 */ /* ip -6 route add 0:2::/64 dev hicn-if1 table 100 */ int _nl_add_in_route_table(const ip_address_t *prefix, const uint32_t interface_id, const uint8_t table_id) { /* Buffer for holding the response, with appropriate casting on the header */ char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; /* Used for send and receive operations on netlink socket */ int fd; size_t n; /* Packet header */ struct { struct nlmsghdr hdr; struct rtmsg payload; } msg = { .hdr.nlmsg_type = RTM_NEWROUTE, .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, .hdr.nlmsg_seq = seq++, .payload.rtm_family = prefix->family, .payload.rtm_dst_len = prefix->prefix_len, // XXX ? XXX dst_len, .payload.rtm_src_len = 0, .payload.rtm_tos = 0, .payload.rtm_table = table_id, /* RT_TABLE_MAIN, etc. */ .payload.rtm_protocol = RTPROT_BOOT, .payload.rtm_scope = prefix->family == AF_INET6 ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, .payload.rtm_type = RTN_UNICAST, .payload.rtm_flags = 0, }; /* Message attributes = length/type/value */ // XXX This could be put directly inside the iovec maybe ? XXX struct rtattr a_dst = {RTA_LENGTH(ip_address_len(prefix)), RTA_DST}; struct rtattr a_oif = {RTA_LENGTH(sizeof(uint32_t)), RTA_OIF}; /* Iovec describing the packets */ struct iovec iov[] = { {&msg, sizeof(msg)}, /* Destination prefix / ip address */ {&a_dst, sizeof(a_dst)}, {(void *)&prefix->buffer, ip_address_len(prefix)}, /* Output interface */ {&a_oif, sizeof(a_oif)}, {(void *)&interface_id, sizeof(uint32_t)}, }; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); /* Open netlink socket */ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR; } /* Send packet */ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR; } /* Receive answer */ n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR; } /* Parse answer */ if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR; } } return HICN_SOCKET_ERROR_NONE; ERR: return HICN_SOCKET_ERROR_UNSPEC; } /* Additional helper functions */ int _nl_add_in_route_table_s(const char *prefix, const uint32_t interface_id, const uint8_t table_id) { int rc; ip_address_t ip_address; rc = hicn_ip_pton(prefix, &ip_address); if (rc < 0) { return rc; } return _nl_add_in_route_table(&ip_address, interface_id, table_id); } int _nl_add_in_route_s(const char *prefix, const uint32_t interface_id) { return _nl_add_in_route_table_s(prefix, interface_id, RT_TABLE_MAIN); } ////////* ip -6 rule add from all prio 10 table local */ /* ip -6 rule add from b001::/16 prio 0 table 100 */ int _nl_add_prio_rule(const ip_address_t *ip_address, uint8_t address_family, const uint32_t priority, const uint8_t table_id) { /* Buffer for holding the response, with appropriate casting on the header */ char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; /* Used for send and receive operations on netlink socket */ int fd; size_t n; /* Packet header */ struct { struct nlmsghdr hdr; struct fib_rule_hdr payload; } msg = { .hdr.nlmsg_type = RTM_NEWRULE, .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, .hdr.nlmsg_seq = seq++, .payload.family = address_family, //.payload.dst_len = , .payload.src_len = ip_address ? ip_address->prefix_len : 0, .payload.tos = 0, .payload.table = table_id, .payload.action = FR_ACT_TO_TBL, .payload.flags = NLM_F_REPLACE, // 0 }; /* Open netlink socket */ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR; } if (ip_address) { /* Message attributes = length/type/value */ struct rtattr a_src = {RTA_LENGTH(ip_address_len(ip_address)), FRA_SRC}; struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; /* Iovec describing the packets */ struct iovec iov[] = { {&msg, sizeof(msg)}, /* Source prefix / ip_address */ {&a_src, sizeof(a_src)}, {(void *)&ip_address->buffer, ip_address_len(ip_address)}, /* Priority */ {&a_prio, sizeof(a_prio)}, {(void *)&priority, sizeof(uint32_t)}, }; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); /* Send packet */ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR; } } else { struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; /* Iovec describing the packets */ struct iovec iov[] = { {&msg, sizeof(msg)}, /* Priority */ {&a_prio, sizeof(a_prio)}, {(void *)&priority, sizeof(uint32_t)}, }; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); /* Send packet */ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR; } } /* Receive answer */ n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR; } /* Parse answer */ if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0) { errno = -err->error; goto ERR; } } return HICN_SOCKET_ERROR_NONE; ERR: return HICN_SOCKET_ERROR_UNSPEC; } int _nl_add_lo_prio_rule(const ip_address_t *ip_address, uint8_t address_family, const uint32_t priority) { return _nl_add_prio_rule(ip_address, address_family, priority, RT_TABLE_LOCAL); } /* ip -6 rule del from all prio 0 table local */ int _nl_del_prio_rule(const ip_address_t *ip_address, uint8_t address_family, const uint32_t priority, const uint8_t table_id) { /* Buffer for holding the response, with appropriate casting on the header */ char buffer[BUFSIZE]; struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; /* Used for send and receive operations on netlink socket */ int fd; size_t n; /* Packet header */ struct { struct nlmsghdr hdr; struct fib_rule_hdr payload; } msg = { .hdr.nlmsg_type = RTM_DELRULE, .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, .hdr.nlmsg_seq = seq++, .payload.family = address_family, //.payload.dst_len = , .payload.src_len = ip_address ? ip_address->prefix_len : 0, .payload.tos = 0, .payload.table = table_id, .payload.action = FR_ACT_TO_TBL, .payload.flags = NLM_F_REPLACE, // 0 }; /* Open netlink socket */ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd < 0) { goto ERR; } /* Message attributes = length/type/value */ if (ip_address) { struct rtattr a_src = {RTA_LENGTH(ip_address_len(ip_address)), FRA_SRC}; struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; /* Iovec describing the packets */ struct iovec iov[] = { {&msg, sizeof(msg)}, /* Source prefix / ip_address */ {&a_src, sizeof(a_src)}, {(void *)&ip_address->buffer, ip_address_len(ip_address)}, /* Priority */ {&a_prio, sizeof(a_prio)}, {(void *)&priority, sizeof(uint32_t)}, }; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); /* Send packet */ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR; } } else { struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; /* Iovec describing the packets */ struct iovec iov[] = { {&msg, sizeof(msg)}, /* Priority */ {&a_prio, sizeof(a_prio)}, {(void *)&priority, sizeof(uint32_t)}, }; msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); /* Send packet */ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); if (n == -1) { goto ERR; } } /* Receive answer */ n = recv(fd, buffer, BUFSIZE, 0); if (n == -1) { goto ERR; } /* Parse answer */ if (hdr->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); if (err->error < 0 && err->error != -2) { //-2 is not such file or directory errno = -err->error; goto ERR; } } return HICN_SOCKET_ERROR_NONE; ERR: return HICN_SOCKET_ERROR_UNSPEC; } int _nl_del_lo_prio_rule(const ip_address_t *ip_address, uint8_t address_family, const uint32_t priority) { return _nl_del_prio_rule(ip_address, address_family, priority, RT_TABLE_LOCAL); } /******************************************************************************/ // #include // duplicate declarations, in the meantime #define IF_NAMESIZE 16 //#define WITH_TUN_PI 1 #ifdef WITH_TUN_PI #define TUN_FLAGS IFF_TUN #else #define TUN_FLAGS IFF_TUN | IFF_NO_PI #endif /* * Taken from Kernel Documentation/networking/tuntap.txt */ int tun_alloc(char *dev, int flags) { struct ifreq ifr; int fd, err; char *clonedev = "/dev/net/tun"; /* Arguments taken by the function: * * char *dev: the name of an interface (or '\0'). MUST have enough * space to hold the interface name if '\0' is passed * int flags: interface flags (eg, IFF_TUN etc.) */ /* open the clone device */ if ((fd = open(clonedev, O_RDWR)) < 0) { return fd; } /* preparation of the struct ifr, of type "struct ifreq" */ memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = flags; if (*dev) { /* if a device name was specified, put it in the structure; otherwise, * the kernel will try to allocate the "next" device of the * specified type */ strncpy(ifr.ifr_name, dev, IF_NAMESIZE - 1); } /* try to create the device */ if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) { close(fd); return err; } /* if the operation was successful, write back the name of the * interface to the variable "dev", so the caller can know * it. Note that the caller MUST reserve space in *dev (see calling * code below) */ strcpy(dev, ifr.ifr_name); /* this is the special file descriptor that the caller will use to talk * with the virtual interface */ return fd; } int linux_get_tun_name(const char *prefix, const char *identifier, char *tun_name) { snprintf(tun_name, IF_NAMESIZE, "%s-%s", prefix, identifier ? identifier : "main"); return 0; } int linux_tun_enable_offload(int fd) { unsigned int offload = 0, tso4 = 1, tso6 = 1, ecn = 1, ufo = 1, csum = 1; /* Check if our kernel supports TUNSETOFFLOAD */ if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) { goto ERR_TUN; } if (csum) { offload |= TUN_F_CSUM; if (tso4) offload |= TUN_F_TSO4; if (tso6) offload |= TUN_F_TSO6; if ((tso4 || tso6) && ecn) offload |= TUN_F_TSO_ECN; if (ufo) offload |= TUN_F_UFO; } if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { offload &= ~TUN_F_UFO; if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n", strerror(errno)); } } return 0; ERR_TUN: return -1; } int linux_tun_create(char *name) { int fd, rc; fd = tun_alloc(name, TUN_FLAGS); if (fd < 0) { // ERROR("Error connecting to tun/tap interface %s!", name); errno = -2; goto ERR_TUN; } rc = linux_tun_enable_offload(fd); if (rc < 0) { // WARN("Could not enable hardware offload on TUN device"); } else { // INFO("Enabled hardware offload on TUN device"); } return fd; ERR_TUN: return -1; } /* * * interface name can be NULL for all interfaces */ int linux_enable_proc(char *path) { int ret = 0; int fd; fd = open(path, O_WRONLY); if (fd < 0) { return -1; } if (write(fd, "1", 1) != 1) { ret = -2; } close(fd); return ret; } int linux_enable_v4_forwarding() { return linux_enable_proc("/proc/sys/net/ipv4/ip_forward"); } int linux_enable_v6_forwarding(char *interface_name) { char path[PATH_MAX]; snprintf(path, PATH_MAX, "/proc/sys/net/ipv6/conf/%s/forwarding", (interface_name) ? interface_name : "all"); return linux_enable_proc(path); } int linux_enable_ndp_proxy() { return linux_enable_proc("/proc/sys/net/ipv6/conf/all/proxy_ndp"); } const hicn_socket_ops_t ops = { .arch = "linux", .get_tun_name = linux_get_tun_name, .tun_create = linux_tun_create, .enable_v4_forwarding = linux_enable_v4_forwarding, .enable_v6_forwarding = linux_enable_v6_forwarding, .enable_ndp_proxy = linux_enable_ndp_proxy, .get_ifid = _nl_get_ifid, .get_output_ifid = _nl_get_output_ifid, .get_ip_addr = _nl_get_ip_addr, .set_ip_addr = _nl_set_ip_addr, .up_if = _nl_up_if, .add_in_route_table = _nl_add_in_route_table, .add_in_route_table_s = _nl_add_in_route_table_s, .add_in_route_s = _nl_add_in_route_s, .add_out_route = _nl_add_out_route, .del_out_route = _nl_del_out_route, .del_lo_route = _nl_del_lo_route, .add_rule = _nl_add_rule, .del_rule = _nl_del_rule, .add_neigh_proxy = _nl_add_neigh_proxy, .add_prio_rule = _nl_add_prio_rule, .add_lo_prio_rule = _nl_add_lo_prio_rule, .del_prio_rule = _nl_del_prio_rule, .del_lo_prio_rule = _nl_del_lo_prio_rule, };