static int add_adresses(int fd, int if_loopback, unsigned *requests) { union { struct sockaddr sa; struct sockaddr_nl nl; } sa; union { struct nlmsghdr header; uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + RTA_LENGTH(sizeof(struct in6_addr))]; } request; struct ifaddrmsg *ifaddrmsg; uint32_t ipv4_address = htonl(INADDR_LOOPBACK); int r; zero(request); request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); request.header.nlmsg_type = RTM_NEWADDR; request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK; request.header.nlmsg_seq = *requests + 1; ifaddrmsg = NLMSG_DATA(&request.header); ifaddrmsg->ifa_family = AF_INET; ifaddrmsg->ifa_prefixlen = 8; ifaddrmsg->ifa_flags = IFA_F_PERMANENT; ifaddrmsg->ifa_scope = RT_SCOPE_HOST; ifaddrmsg->ifa_index = if_loopback; if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address))) < 0) return r; zero(sa); sa.nl.nl_family = AF_NETLINK; if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) return -errno; (*requests)++; if (!socket_ipv6_is_supported()) return 0; request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); request.header.nlmsg_seq = *requests + 1; ifaddrmsg->ifa_family = AF_INET6; ifaddrmsg->ifa_prefixlen = 128; if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback))) < 0) return r; if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) return -errno; (*requests)++; return 0; }
int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key) { const NLTypeSystemUnion *type_system_union; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type); if (r < 0) return r; r = type_system_union_get_type_system(type_system_union, &m->containers[m->n_containers + 1].type_system, key); if (r < 0) return r; r = sd_netlink_message_append_string(m, type_system_union->match, key); if (r < 0) return r; /* do we evere need non-null size */ r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0); if (r < 0) return r; m->containers[m->n_containers ++].offset = r; return 0; }
int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data) { size_t length, size; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(data, -EINVAL); r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING); if (r < 0) return r; if (size) { length = strnlen(data, size+1); if (length > size) return -EINVAL; } else length = strlen(data); r = add_rtattr(m, type, data, length + 1); if (r < 0) return r; return 0; }
int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) { size_t size; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -ERANGE); r = message_attribute_has_type(m, type, NLA_NESTED); if (r < 0) return r; else size = (size_t)r; r = type_system_get_type_system(m->container_type_system[m->n_containers], &m->container_type_system[m->n_containers + 1], type); if (r < 0) return r; r = add_rtattr(m, type | NLA_F_NESTED, NULL, size); if (r < 0) return r; m->container_offsets[m->n_containers ++] = r; return 0; }
int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) { int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); r = add_rtattr(m, type, data, len); if (r < 0) return r; return 0; }
int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data) { int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); if (r < 0) return r; r = add_rtattr(m, type, &data, sizeof(uint32_t)); if (r < 0) return r; return 0; }
int sd_rtnl_message_append_u16(sd_rtnl_message *m, unsigned short type, uint16_t data) { int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); r = message_attribute_has_type(m, type, NLA_U16); if (r < 0) return r; r = add_rtattr(m, type, &data, sizeof(uint16_t)); if (r < 0) return r; return 0; }
int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type) { size_t size; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -ERANGE); r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED); if (r < 0) { const NLTypeSystemUnion *type_system_union; int family; r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_UNION); if (r < 0) return r; r = sd_rtnl_message_get_family(m, &family); if (r < 0) return r; r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type); if (r < 0) return r; r = type_system_union_protocol_get_type_system(type_system_union, &m->containers[m->n_containers + 1].type_system, family); if (r < 0) return r; } else { r = type_system_get_type_system(m->containers[m->n_containers].type_system, &m->containers[m->n_containers + 1].type_system, type); if (r < 0) return r; } r = add_rtattr(m, type | NLA_F_NESTED, NULL, size); if (r < 0) return r; m->containers[m->n_containers ++].offset = r; return 0; }
int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) { int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(info, -EINVAL); r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); if (r < 0) return r; r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo)); if (r < 0) return r; return 0; }
int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) { int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(data, -EINVAL); r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); if (r < 0) return r; r = add_rtattr(m, type, data, ETH_ALEN); if (r < 0) return r; return 0; }
int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) { size_t size; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG); if (r < 0) return r; r = add_rtattr(m, type, NULL, 0); if (r < 0) return r; return 0; }
int sd_rtnl_message_append_in6_addr(sd_rtnl_message *m, unsigned short type, const struct in6_addr *data) { int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(data, -EINVAL); r = message_attribute_has_type(m, type, NLA_IN_ADDR); if (r < 0) return r; r = add_rtattr(m, type, data, sizeof(struct in6_addr)); if (r < 0) return r; return 0; }
static int add_adresses(int fd, int if_loopback, unsigned *requests) { union { struct sockaddr sa; struct sockaddr_nl nl; } sa = { .nl.nl_family = AF_NETLINK, }; union { struct nlmsghdr header; uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + RTA_LENGTH(sizeof(struct in6_addr))]; } request = { .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), .header.nlmsg_type = RTM_NEWADDR, .header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK, .header.nlmsg_seq = *requests + 1, }; struct ifaddrmsg *ifaddrmsg; uint32_t ipv4_address = htonl(INADDR_LOOPBACK); int r; ifaddrmsg = NLMSG_DATA(&request.header); ifaddrmsg->ifa_family = AF_INET; ifaddrmsg->ifa_prefixlen = 8; ifaddrmsg->ifa_flags = IFA_F_PERMANENT; ifaddrmsg->ifa_scope = RT_SCOPE_HOST; ifaddrmsg->ifa_index = if_loopback; r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address)); if (r < 0) return r; if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) return -errno; (*requests)++; if (!socket_ipv6_is_supported()) return 0; request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); request.header.nlmsg_seq = *requests + 1; ifaddrmsg->ifa_family = AF_INET6; ifaddrmsg->ifa_prefixlen = 128; r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback)); if (r < 0) return r; if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) return -errno; (*requests)++; return 0; } static int start_interface(int fd, int if_loopback, unsigned *requests) { union { struct sockaddr sa; struct sockaddr_nl nl; } sa = { .nl.nl_family = AF_NETLINK, }; union { struct nlmsghdr header; uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))]; } request = { .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), .header.nlmsg_type = RTM_NEWLINK, .header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK, .header.nlmsg_seq = *requests + 1, }; struct ifinfomsg *ifinfomsg; ifinfomsg = NLMSG_DATA(&request.header); ifinfomsg->ifi_family = AF_UNSPEC; ifinfomsg->ifi_index = if_loopback; ifinfomsg->ifi_flags = IFF_UP; ifinfomsg->ifi_change = IFF_UP; if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) return -errno; (*requests)++; return 0; } static int read_response(int fd, unsigned requests_max) { union { struct sockaddr sa; struct sockaddr_nl nl; } sa; union { struct nlmsghdr header; uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct nlmsgerr))]; } response; ssize_t l; socklen_t sa_len = sizeof(sa); struct nlmsgerr *nlmsgerr; l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len); if (l < 0) return -errno; if (sa_len != sizeof(sa.nl) || sa.nl.nl_family != AF_NETLINK) return -EIO; if (sa.nl.nl_pid != 0) return 0; if ((size_t) l < sizeof(struct nlmsghdr)) return -EIO; if (response.header.nlmsg_type != NLMSG_ERROR || (pid_t) response.header.nlmsg_pid != getpid() || response.header.nlmsg_seq >= requests_max) return 0; if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) || response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) return -EIO; nlmsgerr = NLMSG_DATA(&response.header); if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST) return nlmsgerr->error; return response.header.nlmsg_seq; } static int check_loopback(void) { int r; _cleanup_close_ int fd; union { struct sockaddr sa; struct sockaddr_in in; } sa = { .in.sin_family = AF_INET, .in.sin_addr.s_addr = INADDR_LOOPBACK, }; /* If we failed to set up the loop back device, check whether * it might already be set up */ fd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); if (fd < 0) return -errno; if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0) r = 1; else r = errno == EADDRNOTAVAIL ? 0 : -errno; return r; } int loopback_setup(void) { int r, if_loopback; union { struct sockaddr sa; struct sockaddr_nl nl; } sa = { .nl.nl_family = AF_NETLINK, }; unsigned requests = 0, i; _cleanup_close_ int fd = -1; bool eperm = false; errno = 0; if_loopback = (int) if_nametoindex("lo"); if (if_loopback <= 0) return errno ? -errno : -ENODEV; fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) return -errno; if (bind(fd, &sa.sa, sizeof(sa)) < 0) { r = -errno; goto error; } r = add_adresses(fd, if_loopback, &requests); if (r < 0) goto error; r = start_interface(fd, if_loopback, &requests); if (r < 0) goto error; for (i = 0; i < requests; i++) { r = read_response(fd, requests); if (r == -EPERM) eperm = true; else if (r < 0) goto error; } if (eperm && check_loopback() < 0) { r = -EPERM; goto error; } return 0; error: log_warning("Failed to configure loopback device: %s", strerror(-r)); return r; }