static struct snobj *handle_create_module(struct snobj *arg) { const char *mclass_name; const struct mclass *mclass; struct module *module; struct snobj *r; mclass_name = snobj_eval_str(arg, "mclass"); if (!mclass_name) return snobj_err(EINVAL, "Missing 'mclass' field in arg"); mclass = find_mclass(mclass_name); if (!mclass) return snobj_err(ENOENT, "No mclass '%s' found", mclass_name); module = create_module(snobj_eval_str(arg, "name"), mclass, snobj_eval(arg, "arg"), &r); if (!module) return r; printf("Module %s created at %p\n", module->name, module); r = snobj_map(); snobj_map_set(r, "name", snobj_str(module->name)); return r; }
static struct snobj *handle_add_worker(struct snobj *q) { unsigned int wid; unsigned int core; struct snobj *t; t = snobj_eval(q, "wid"); if (!t) return snobj_err(EINVAL, "Missing 'wid' field"); wid = snobj_uint_get(t); if (wid >= MAX_WORKERS) return snobj_err(EINVAL, "'wid' must be between 0 and %d", MAX_WORKERS - 1); t = snobj_eval(q, "core"); if (!t) return snobj_err(EINVAL, "Missing 'core' field"); core = snobj_uint_get(t); if (!is_cpu_present(core)) return snobj_err(EINVAL, "Invalid core %d", core); if (is_worker_active(wid)) return snobj_err(EEXIST, "worker:%d is already active", wid); launch_worker(wid, core); return NULL; }
static struct snobj *handle_enable_tcpdump(struct snobj *q) { const char *m_name; const char *fifo; gate_t gate; struct module *m; int ret; m_name = snobj_eval_str(q, "name"); gate = snobj_eval_uint(q, "gate"); fifo = snobj_eval_str(q, "fifo"); if (!m_name) return snobj_err(EINVAL, "Missing 'name' field"); if ((m = find_module(m_name)) == NULL) return snobj_err(ENOENT, "No module '%s' found", m_name); if (gate >= m->allocated_gates) return snobj_err(EINVAL, "Gate '%hu' does not exist", gate); ret = enable_tcpdump(fifo, m, gate); if (ret < 0) { return snobj_err(-ret, "Enabling tcpdump %s[%d] failed", m_name, gate); } return NULL; }
static struct snobj *handle_create_port(struct snobj *arg) { const char *driver_name; const struct driver *driver; struct port *port; struct snobj *r; struct snobj *err; driver_name = snobj_eval_str(arg, "driver"); if (!driver_name) return snobj_err(EINVAL, "Missing 'driver' field in arg"); driver = find_driver(driver_name); if (!driver) return snobj_err(ENOENT, "No port driver '%s' found", driver_name); port = create_port(snobj_eval_str(arg, "name"), driver, snobj_eval(arg, "arg"), &err); if (!port) return err; printf("Port %s created at %p\n", port->name, port); r = snobj_map(); snobj_map_set(r, "name", snobj_str(port->name)); return r; }
static struct snobj *handle_connect_modules(struct snobj *arg) { const char *m1_name; const char *m2_name; uint16_t gate; struct module *m1; struct module *m2; int ret; m1_name = snobj_eval_str(arg, "m1"); m2_name = snobj_eval_str(arg, "m2"); gate = snobj_eval_uint(arg, "gate"); if (!m1_name || !m2_name) return snobj_err(EINVAL, "Missing 'm1' or 'm2' field in arg"); if ((m1 = find_module(m1_name)) == NULL) return snobj_err(ENOENT, "No module '%s' found", m1_name); if ((m2 = find_module(m2_name)) == NULL) return snobj_err(ENOENT, "No module '%s' found", m2_name); ret = connect_modules(m1, gate, m2); if (ret < 0) return snobj_err(-ret, "Connection '%s'[%d]->'%s' failed", m1_name, gate, m2_name); printf("%s[%d] -> %s\n", m1_name, gate, m2_name); return NULL; }
static struct snobj *handle_get_tc_stats(struct snobj *q) { const char *tc_name; struct tc *c; struct snobj *r; tc_name = snobj_str_get(q); if (!tc_name) return snobj_err(EINVAL, "Argument must be a name in str"); c = ns_lookup(NS_TYPE_TC, tc_name); if (!c) return snobj_err(ENOENT, "No TC '%s' found", tc_name); r = snobj_map(); snobj_map_set(r, "timestamp", snobj_double(get_epoch_time())); snobj_map_set(r, "count", snobj_uint(c->stats.usage[RESOURCE_CNT])); snobj_map_set(r, "cycles", snobj_uint(c->stats.usage[RESOURCE_CYCLE])); snobj_map_set(r, "packets", snobj_uint(c->stats.usage[RESOURCE_PACKET])); snobj_map_set(r, "bits", snobj_uint(c->stats.usage[RESOURCE_BIT])); return r; }
static struct snobj *handle_disable_tcpdump(struct snobj *arg) { const char *m_name; uint16_t gate; struct module *m; int ret; m_name = snobj_eval_str(arg, "name"); gate = snobj_eval_uint(arg, "gate"); if (!m_name) return snobj_err(EINVAL, "Missing 'name' field in arg"); if ((m = find_module(m_name)) == NULL) return snobj_err(ENOENT, "No module '%s' found", m_name); if (gate >= m->allocated_gates) return snobj_err(EINVAL, "Gate '%hu' does not exist", gate); ret = disable_tcpdump(m, gate); if (ret < 0) { return snobj_err(-ret, "Disabling tcpdump %s[%d] failed", m_name, gate); } return NULL; }
static struct snobj *handle_disconnect_modules(struct snobj *arg) { const char *m_name; uint16_t gate; struct module *m; int ret; m_name = snobj_eval_str(arg, "name"); gate = snobj_eval_uint(arg, "gate"); if (!m_name) return snobj_err(EINVAL, "Missing 'name' field in arg"); if ((m = find_module(m_name)) == NULL) return snobj_err(ENOENT, "No module '%s' found", m_name); ret = disconnect_modules(m, gate); if (ret < 0) return snobj_err(-ret, "Disconnection '%s'[%d] failed", m_name, gate); printf("%s[%d] -> <dead end>\n", m_name, gate); return NULL; }
static struct snobj *queue_out_init(struct module *m, struct snobj *arg) { struct queue_out_priv *priv = get_priv(m); struct snobj *t; const char *port_name; int ret; if (!arg || snobj_type(arg) != TYPE_MAP) return snobj_err(EINVAL, "Argument must be a map"); t = snobj_eval(arg, "port"); if (!t || !(port_name = snobj_str_get(t))) return snobj_err(EINVAL, "Field 'port' must be specified"); t = snobj_eval(arg, "qid"); if (!t || snobj_type(t) != TYPE_INT) return snobj_err(EINVAL, "Field 'qid' must be specified"); priv->qid = snobj_uint_get(t); priv->port = find_port(port_name); if (!priv->port) return snobj_err(ENODEV, "Port %s not found", port_name); ret = acquire_queues(priv->port, m, PACKET_DIR_OUT, &priv->qid, 1); if (ret < 0) return snobj_errno(-ret); priv->send_pkts = priv->port->driver->send_pkts; return NULL; }
static struct snobj *docker_container_pid(char *cid, int *container_pid) { char buf[1024]; FILE *fp; int ret; int exit_code; if (!cid) return snobj_err(EINVAL, "field 'docker' should be " \ "a containder ID or name in string"); ret = snprintf(buf, sizeof(buf), "docker inspect --format '{{.State.Pid}}' " \ "%s 2>&1", cid); if (ret >= sizeof(buf)) return snobj_err(EINVAL, "The specified Docker " \ "container ID or name is too long"); fp = popen(buf, "r"); if (!fp) return snobj_err_details(ESRCH, snobj_str_fmt("popen() errno=%d (%s)", errno, strerror(errno)), "Command 'docker' is not available. " \ "(not installed?)"); ret = fread(buf, 1, sizeof(buf) - 1, fp); if (ret == 0) return snobj_err(ENOENT, "Cannot find the PID of " \ "container %s", cid); buf[ret] = '\0'; ret = pclose(fp); exit_code = WEXITSTATUS(ret); if (exit_code != 0 || sscanf(buf, "%d", container_pid) == 0) { struct snobj *details = snobj_map(); snobj_map_set(details, "exit_code", snobj_int(exit_code)); snobj_map_set(details, "docker_err", snobj_str(buf)); return snobj_err_details(ESRCH, details, "Cannot find the PID of container %s", cid); } return NULL; }
static struct snobj *handle_get_module_info(struct snobj *arg) { const char *m_name; struct module *m; struct snobj *r; struct snobj *gates; m_name = snobj_str_get(arg); if (!m_name) return snobj_err(EINVAL, "Argument must be a name in str"); if ((m = find_module(m_name)) == NULL) return snobj_err(ENOENT, "No module '%s' found", m_name); r = snobj_map(); gates = snobj_list(); snobj_map_set(r, "name", snobj_str(m->name)); snobj_map_set(r, "mclass", snobj_str(m->mclass->name)); if (m->mclass->get_desc) snobj_map_set(r, "desc", m->mclass->get_desc(m)); if (m->mclass->get_dump) snobj_map_set(r, "dump", m->mclass->get_dump(m)); for (int i = 0; i < m->allocated_gates; i++) { if (m->gates[i].m) { struct snobj *gate = snobj_map(); snobj_map_set(gate, "gate", snobj_uint(i)); #if TRACK_GATES snobj_map_set(gate, "cnt", snobj_uint(m->gates[i].cnt)); snobj_map_set(gate, "pkts", snobj_uint(m->gates[i].pkts)); snobj_map_set(gate, "timestamp", snobj_double(get_epoch_time())); #endif snobj_map_set(gate, "name", snobj_str(m->gates[i].m->name)); snobj_list_add(gates, gate); } } snobj_map_set(r, "gates", gates); return r; }
static struct snobj *handle_attach_task(struct snobj *q) { const char *m_name; const char *tc_name; task_id_t tid; struct module *m; struct task *t; m_name = snobj_eval_str(q, "name"); if (!m_name) return snobj_err(EINVAL, "Missing 'name' field"); if ((m = find_module(m_name)) == NULL) return snobj_err(ENOENT, "No module '%s' found", m_name); tid = snobj_eval_uint(q, "taskid"); if (tid >= MAX_TASKS_PER_MODULE) return snobj_err(EINVAL, "'taskid' must be between 0 and %d", MAX_TASKS_PER_MODULE - 1); if ((t = m->tasks[tid]) == NULL) return snobj_err(ENOENT, "Task %s:%hu does not exist", m_name, tid); tc_name = snobj_eval_str(q, "tc"); if (tc_name) { struct tc *c; c = ns_lookup(NS_TYPE_TC, tc_name); if (!c) return snobj_err(ENOENT, "No TC '%s' found", tc_name); task_attach(t, c); } else { int wid; /* TODO: worker_id_t */ if (task_is_attached(t)) return snobj_err(EBUSY, "Task %s:%hu is already " "attached to a TC", m_name, tid); wid = snobj_eval_uint(q, "wid"); if (wid >= MAX_WORKERS) return snobj_err(EINVAL, "'wid' must be between 0 and %d", MAX_WORKERS - 1); if (!is_worker_active(wid)) return snobj_err(EINVAL, "Worker %d does not exist", wid); assign_default_tc(wid, t); } return NULL; }
static struct snobj *handle_destroy_module(struct snobj *arg) { const char *m_name; struct module *m; m_name = snobj_str_get(arg); if (!m_name) return snobj_err(EINVAL, "Argument must be a name in str"); if ((m = find_module(m_name)) == NULL) return snobj_err(ENOENT, "No module '%s' found", m_name); destroy_module(m); return NULL; }
static struct snobj *port_out_init(struct module *m, struct snobj *arg) { struct port_out_priv *priv = get_priv(m); const char *port_name; port_name = snobj_str_get(arg); if (!port_name) return snobj_err(EINVAL, "Argument must be a string"); priv->port = find_port(port_name); if (!priv->port) return snobj_err(ENODEV, "Port %s not found", port_name); acquire_queue(priv->port, PACKET_DIR_OUT, 0 /* XXX */, m); return NULL; }
struct snobj *handle_request(struct client *c, struct snobj *q) { struct snobj *r = NULL; const char *s; #if 0 printf("Request:\n"); snobj_dump(q); #endif if (q->type != TYPE_MAP) { r = snobj_err(EINVAL, "The message must be a map"); goto reply; } s = snobj_eval_str(q, "to"); if (!s) { r = snobj_str("There is no 'to' field"); goto reply; } if (strcmp(s, "softnic") == 0) { r = handle_snobj_softnic(q); } else if (strcmp(s, "module") == 0) { r = handle_snobj_module(q); } else r = snobj_err(EINVAL, "Unknown destination in 'to': %s", s); reply: /* No response was made? (normally means "success") */ if (!r) r = snobj_nil(); #if 0 printf("Response:\n"); snobj_dump(r); #endif return r; }
static struct snobj *handle_get_port_stats(struct snobj *arg) { const char *port_name; struct port *port; port_stats_t stats; struct snobj *r; struct snobj *inc; struct snobj *out; port_name = snobj_str_get(arg); if (!port_name) return snobj_err(EINVAL, "Argument must be a name in str"); port = find_port(port_name); if (!port) return snobj_err(ENOENT, "No port `%s' found", port_name); get_port_stats(port, &stats); inc = snobj_map(); snobj_map_set(inc, "packets", snobj_uint(stats[PACKET_DIR_INC].packets)); snobj_map_set(inc, "dropped", snobj_uint(stats[PACKET_DIR_INC].dropped)); snobj_map_set(inc, "bytes", snobj_uint(stats[PACKET_DIR_INC].bytes)); out = snobj_map(); snobj_map_set(out, "packets", snobj_uint(stats[PACKET_DIR_OUT].packets)); snobj_map_set(out, "dropped", snobj_uint(stats[PACKET_DIR_OUT].dropped)); snobj_map_set(out, "bytes", snobj_uint(stats[PACKET_DIR_OUT].bytes)); r = snobj_map(); snobj_map_set(r, "inc", inc); snobj_map_set(r, "out", out); snobj_map_set(r, "timestamp", snobj_double(get_epoch_time())); return r; }
static struct snobj *handle_destroy_port(struct snobj *arg) { const char *port_name; struct port *port; int ret; port_name = snobj_str_get(arg); if (!port_name) return snobj_err(EINVAL, "Argument must be a name in str"); port = find_port(port_name); if (!port) return snobj_err(ENOENT, "No port `%s' found", port_name); ret = destroy_port(port); if (ret) return snobj_errno(-ret); return NULL; }
static struct snobj *handle_snobj_module(struct snobj *q) { const char *m_name; const char *cmd; struct module *m; m_name = snobj_eval_str(q, "name"); if (!m_name) return snobj_err(EINVAL, "Missing module name field 'name'"); if ((m = find_module(m_name)) == NULL) return snobj_err(ENOENT, "No module '%s' found", m_name); cmd = snobj_eval_str(q, "cmd"); if (strcmp(cmd, "query") == 0) { struct snobj *arg; if (!m->mclass->query) return snobj_err(ENOTSUP, "Module '%s' does not support queries", m_name); arg = snobj_eval(q, "arg"); if (!arg) { struct snobj *ret; arg = snobj_nil(); ret = m->mclass->query(m, arg); snobj_free(arg); return ret; } return m->mclass->query(m, arg); } else return snobj_err(ENOTSUP, "Not supported command '%s'", cmd); }
static struct snobj *port_inc_init(struct module *m, struct snobj *arg) { struct port_inc_priv *priv = get_priv(m); const char *port_name; task_id_t ret; if (!arg || !(port_name = snobj_str_get(arg))) return snobj_err(EINVAL, "Argument must be a port name " \ "(string)"); priv->port = find_port(port_name); if (!priv->port) return snobj_err(ENODEV, "Port %s not found", port_name); ret = register_task(m, NULL); if (ret == INVALID_TASK_ID) return snobj_err(ENOMEM, "Task creation failed"); acquire_queue(priv->port, PACKET_DIR_INC, 0 /* XXX */, m); return NULL; }
static struct snobj *handle_snobj_softnic(struct snobj *q) { struct snobj *arg; const char *s; s = snobj_eval_str(q, "cmd"); if (!s) return snobj_err(EINVAL, "Missing 'cmd' field"); arg = snobj_map_get(q, "arg"); for (int i = 0; sn_handlers[i].cmd != NULL; i++) { if (strcmp(s, sn_handlers[i].cmd) != 0) continue; if (sn_handlers[i].pause_needed && is_any_worker_running()) return snobj_err(EBUSY, "There is a running worker"); return sn_handlers[i].func(arg); } return snobj_err(ENOTSUP, "Unknown command in 'cmd': '%s'", s); }
static struct snobj *handle_add_tc(struct snobj *q) { const char *tc_name; int wid; struct tc_params params; struct tc *c; tc_name = snobj_eval_str(q, "name"); if (!tc_name) return snobj_err(EINVAL, "Missing 'name' field"); if (!ns_is_valid_name(tc_name)) return snobj_err(EINVAL, "'%s' is an invalid name", tc_name); if (ns_name_exists(tc_name)) return snobj_err(EINVAL, "Name '%s' already exists", tc_name); wid = snobj_eval_uint(q, "wid"); if (wid >= MAX_WORKERS) return snobj_err(EINVAL, "'wid' must be between 0 and %d", MAX_WORKERS - 1); if (!is_worker_active(wid)) return snobj_err(EINVAL, "worker:%d does not exist", wid); memset(¶ms, 0, sizeof(params)); strcpy(params.name, tc_name); params.priority = snobj_eval_int(q, "priority"); if (params.priority == DEFAULT_PRIORITY) return snobj_err(EINVAL, "Priority %d is reserved", DEFAULT_PRIORITY); /* TODO */ params.share = 1; params.share_resource = RESOURCE_CNT; c = tc_init(workers[wid]->s, ¶ms); if (is_err(c)) return snobj_err(-ptr_to_err(c), "tc_init() failed"); tc_join(c); return NULL; }
static struct snobj *handle_reset_tcs(struct snobj *q) { struct ns_iter iter; struct tc *c; struct tc **c_arr; size_t arr_slots = 1024; int n = 0; c_arr = malloc(arr_slots * sizeof(struct tc *)); ns_init_iterator(&iter, NS_TYPE_TC); while ((c = (struct tc *)ns_next(&iter)) != NULL) { if (n >= arr_slots) { arr_slots *= 2; c_arr = realloc(c_arr, arr_slots * sizeof(struct tc *)); } c_arr[n] = c; n++; } ns_release_iterator(&iter); for (int i = 0; i < n; i++) { c = c_arr[i]; if (c->num_tasks) { free(c_arr); return snobj_err(EBUSY, "TC %s still has %d tasks", c->name, c->num_tasks); } if (c->auto_free) continue; tc_leave(c); tc_dec_refcnt(c); } free(c_arr); return NULL; }
static struct snobj *handle_not_implemented(struct snobj *q) { return snobj_err(ENOTSUP, "Not implemented yet"); }
static struct snobj *handle_list_tcs(struct snobj *q) { struct snobj *r; struct snobj *t; unsigned int wid_filter = MAX_WORKERS; struct ns_iter iter; struct tc *c; t = snobj_eval(q, "wid"); if (t) { wid_filter = snobj_uint_get(t); if (wid_filter >= MAX_WORKERS) return snobj_err(EINVAL, "'wid' must be between 0 and %d", MAX_WORKERS - 1); if (!is_worker_active(wid_filter)) return snobj_err(EINVAL, "worker:%d does not exist", wid_filter); } r = snobj_list(); ns_init_iterator(&iter, NS_TYPE_TC); while ((c = (struct tc *)ns_next(&iter)) != NULL) { int wid; if (wid_filter < MAX_WORKERS) { if (workers[wid_filter]->s != c->s) continue; wid = wid_filter; } else { for (wid = 0; wid < MAX_WORKERS; wid++) if (is_worker_active(wid) && workers[wid]->s == c->s) break; } struct snobj *elem = snobj_map(); snobj_map_set(elem, "name", snobj_str(c->name)); snobj_map_set(elem, "tasks", snobj_int(c->num_tasks)); snobj_map_set(elem, "parent", snobj_str(c->parent->name)); snobj_map_set(elem, "priority", snobj_int(c->priority)); if (wid < MAX_WORKERS) snobj_map_set(elem, "wid", snobj_uint(wid)); else snobj_map_set(elem, "wid", snobj_int(-1)); snobj_list_add(r, elem); } ns_release_iterator(&iter); return r; }
static struct snobj *set_ip_addr(struct port *p, int container_pid, struct snobj *arg) { int child_pid; int ret = 0; if (snobj_type(arg) == TYPE_STR || snobj_type(arg) == TYPE_LIST) { if (snobj_type(arg) == TYPE_LIST) { if (arg->size == 0) goto invalid_type; for (int i = 0; i < arg->size; i++) { struct snobj *addr = snobj_list_get(arg, i); if (snobj_type(addr) != TYPE_STR) goto invalid_type; } } } else goto invalid_type; /* change network namespace if necessary */ if (container_pid) { child_pid = fork(); if (child_pid < 0) return snobj_errno(-child_pid); if (child_pid == 0) { char buf[1024]; int fd; sprintf(buf, "/proc/%d/ns/net", container_pid); fd = open(buf, O_RDONLY); if (fd < 0) { perror("open(/proc/pid/ns/net)"); exit(errno <= 255 ? errno: ENOMSG); } ret = setns(fd, 0); if (ret < 0) { perror("setns()"); exit(errno <= 255 ? errno: ENOMSG); } } else goto wait_child; } switch (snobj_type(arg)) { case TYPE_STR: ret = set_ip_addr_single(p, snobj_str_get(arg)); if (ret < 0) { if (container_pid) { /* it must be the child */ assert(child_pid == 0); exit(errno <= 255 ? errno: ENOMSG); } } break; case TYPE_LIST: if (!arg->size) goto invalid_type; for (int i = 0; i < arg->size; i++) { struct snobj *addr = snobj_list_get(arg, i); ret = set_ip_addr_single(p, snobj_str_get(addr)); if (ret < 0) { if (container_pid) { /* it must be the child */ assert(child_pid == 0); exit(errno <= 255 ? errno: ENOMSG); } else break; } } break; default: assert(0); } if (container_pid) { if (child_pid == 0) { if (ret < 0) { ret = -ret; exit(ret <= 255 ? ret: ENOMSG); } else exit(0); } else { int exit_status; wait_child: ret = waitpid(child_pid, &exit_status, 0); if (ret >= 0) { assert(ret == child_pid); ret = -WEXITSTATUS(exit_status); } else perror("waitpid()"); } } if (ret < 0) return snobj_err_details(-ret, arg, "Failed to set IP addresses " \ "(incorrect IP address format?)"); return NULL; invalid_type: return snobj_err(EINVAL, "'ip_addr' must be a string or list " \ "of IPv4/v6 addresses (e.g., '10.0.20.1/24')"); }
static struct snobj *init_port(struct port *p, struct snobj *conf) { struct vport_priv *priv = get_port_priv(p); int container_pid = 0; int cpu; int rxq; int ret; if (strlen(p->name) >= IFNAMSIZ) return snobj_err(EINVAL, "Linux interface name should be " \ "shorter than %d characters", IFNAMSIZ); if (snobj_eval_exists(conf, "docker")) { struct snobj *err = docker_container_pid( snobj_eval_str(conf, "docker"), &container_pid); if (err) return err; } priv->fd = open("/dev/softnic", O_RDONLY); if (priv->fd == -1) return snobj_err(ENODEV, "the kernel module is not loaded"); priv->bar = alloc_bar(p, container_pid); ret = ioctl(priv->fd, SN_IOC_CREATE_HOSTNIC, rte_malloc_virt2phy(priv->bar)); if (ret < 0) { close(priv->fd); return snobj_errno_details(-ret, snobj_str("SN_IOC_CREATE_HOSTNIC failure")); } if (snobj_eval_exists(conf, "ip_addr")) { struct snobj *err = set_ip_addr(p, container_pid, snobj_eval(conf, "ip_addr")); if (err) { deinit_port(p); return err; } } for (cpu = 0; cpu < SN_MAX_CPU; cpu++) priv->map.cpu_to_txq[cpu] = cpu % p->num_queues[PACKET_DIR_INC]; cpu = 0; for (rxq = 0; rxq < p->num_queues[PACKET_DIR_OUT]; rxq++) { while (is_worker_core(cpu)) cpu = (cpu + 1) % sysconf(_SC_NPROCESSORS_ONLN); priv->map.rxq_to_cpu[rxq] = cpu; cpu = (cpu + 1) % sysconf(_SC_NPROCESSORS_ONLN); } ret = ioctl(priv->fd, SN_IOC_SET_QUEUE_MAPPING, &priv->map); if (ret < 0) perror("SN_IOC_SET_QUEUE_MAPPING"); return NULL; }