#include "device.h" #include "netlink.h" #include "util.h" #include "vector.h" #include #include #include #include #include #include #include #include #include #include #include static device_type_t device_type_interface; typedef struct _ipaddr { int af; union { struct in_addr addr4; struct in6_addr addr6; }; } ipaddr_t; typedef struct _ipprefix { ipaddr_t addr; uint8_t plen; } ipaddr_prefix_t; typedef VECTOR(ipaddr_prefix_t) ipaddr_prefix_vector_t; typedef struct _device_interface { device_t device; struct ether_addr macaddr; uint16_t mtu; ipaddr_prefix_vector_t addrs; } device_interface_t; static unsigned long strtoul_safe(const char *str) { char *endptr; errno = 0; unsigned long val = strtoul(str, &endptr, 10); if (endptr == str || *endptr) errno = EINVAL; return val; } static bool parse_address(ipaddr_t *addr, const char *str) { if (inet_pton(AF_INET, str, &addr->addr4)) { addr->af = AF_INET; return true; } if (inet_pton(AF_INET6, str, &addr->addr6)) { addr->af = AF_INET6; return true; } return false; } static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_host) { const char *slash = strrchr(str, '/'); if (!slash) return false; size_t len = slash - str; char buf[len+1]; memcpy(buf, str, len); buf[len] = 0; if (!parse_address(&prefix->addr, buf)) return false; long plen = strtoul_safe(slash + 1); if (errno) return false; switch (prefix->addr.af) { case AF_INET: if (plen > 32) return false; break; case AF_INET6: if (plen > 128) return false; break; default: assert(false); __builtin_unreachable(); } prefix->plen = plen; // TODO: Implement allow_host return true; } static void interface_free(device_t *dev) { device_interface_t *iface = container_of(dev, device_interface_t, device); VECTOR_FREE(iface->addrs); free(NODE_NAME(dev)); free(iface); } static bool process_section_device(device_interface_t *iface, struct json_object *section) { return true; } static bool process_section_static(device_interface_t *iface, struct json_object *section) { struct json_object *addresses = neco_json_get_value(section, "addresses", json_type_array); if (addresses) { for (size_t i = 0; i < json_object_array_length(addresses); i++) { struct json_object *address = json_object_array_get_idx(addresses, i); if (!json_object_is_type(address, json_type_string)) { fprintf(stderr, "interface: static: invalid address entry of type %s\n", json_type_to_name(json_object_get_type(address))); continue; } ipaddr_prefix_t p; if (!parse_prefix(&p, json_object_get_string(address), true)) { fprintf(stderr, "interface: static: unable to parse Address %s\n", json_object_get_string(address)); break; } if (!VECTOR_ADD(iface->addrs, p)) { fprintf(stderr, "interface: static: adding address failed\n"); return false; } } } /* list_for_each_entry(field, §ion->fields, node) { switch (lookup_keyword(field->key)) { case KW_Address: { ipaddr_prefix_t p; if (!parse_prefix(&p, field->value, true)) { fprintf(stderr, "interface: [Static]: unable to parse Address %s\n", field->value); break; } if (!VECTOR_ADD(iface->addrs, p)) { fprintf(stderr, "interface: [Static]: adding address failed\n"); return false; } break; } default: fprintf(stderr, "interface: [Static]: unknown field %s\n", field->key); } } */ return true; } static device_t * interface_process_config(const char *name, struct json_object *config) { device_interface_t *iface = calloc(1, sizeof(*iface)); if (!iface) return NULL; device_t *dev = &iface->device; dev->type = &device_type_interface; NODE_NAME(dev) = strdup(name); if (!NODE_NAME(dev)) { free(iface); return NULL; } struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object); if (sec_device) { if (!process_section_device(iface, sec_device)) goto err; } struct json_object *sec_static = neco_json_get_value(config, "static", json_type_object); if (sec_static) { if (!process_section_static(iface, sec_static)) goto err; } return dev; err: interface_free(dev); return NULL; } static bool interface_set_link_flags(unsigned index, unsigned change, unsigned flags) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct mnl_socket *nl = nl_socket(); struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = RTM_SETLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; int seq = nlh->nlmsg_seq = nl_seq(); struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi)); ifi->ifi_index = index; ifi->ifi_family = AF_UNSPEC; ifi->ifi_change = change; ifi->ifi_flags = flags; if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { perror("mnl_socket_sendto"); return false; } int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); if (ret == -1) { perror("mnl_socket_recvfrom"); return false; } ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); if (ret == -1) { perror("mnl_cb_run"); return false; } return true; } static bool interface_set_link_state(unsigned index, bool up) { return interface_set_link_flags(index, IFF_UP, up ? IFF_UP : 0); } static bool interface_set_ipaddr(unsigned index, ipaddr_prefix_t *addr, bool add) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct mnl_socket *nl = nl_socket(); struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = add ? RTM_NEWADDR : RTM_DELADDR; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; if (add) nlh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; int seq = nlh->nlmsg_seq = nl_seq(); struct ifaddrmsg *ifa = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifa)); ifa->ifa_index = index; ifa->ifa_family = addr->addr.af; ifa->ifa_prefixlen = addr->plen; switch (addr->addr.af) { case AF_INET: mnl_attr_put(nlh, IFA_LOCAL, 4, &addr->addr.addr4); break; case AF_INET6: mnl_attr_put(nlh, IFA_LOCAL, 16, &addr->addr.addr6); break; default: errno = EINVAL; return false; } if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { perror("mnl_socket_sendto"); return false; } int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); if (ret == -1) { perror("mnl_socket_recvfrom"); return false; } ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); if (ret == -1) { perror("mnl_cb_run"); return false; } return true; } static void interface_init(device_t *dev) { device_interface_t *iface = container_of(dev, device_interface_t, device); unsigned index = if_nametoindex(NODE_NAME(dev)); if (!index) return; interface_set_link_state(index, true); for (size_t i = 0; i < VECTOR_LEN(iface->addrs); i++) interface_set_ipaddr(index, &VECTOR_INDEX(iface->addrs, i), true); } static void interface_update(device_t *dev) { } static void interface_release(device_t *dev) { device_interface_t *iface = container_of(dev, device_interface_t, device); unsigned index = if_nametoindex(NODE_NAME(dev)); if (!index) return; for (size_t i = 0; i < VECTOR_LEN(iface->addrs); i++) interface_set_ipaddr(index, &VECTOR_INDEX(iface->addrs, i), false); interface_set_link_state(index, false); } static device_type_t device_type_interface = { .process_config = interface_process_config, .free = interface_free, .init = interface_init, .update = interface_update, .release = interface_release, }; __attribute__((constructor)) static void interface_constructor(void) { register_device_type("interface", &device_type_interface); }