static struct sk_buff *tipc_show_stats(void) { struct sk_buff *buf; struct tlv_desc *rep_tlv; char *pb; int pb_len; int str_len; u32 value; if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); value = ntohl(*(u32 *)TLV_DATA(req_tlv_area)); if (value != 0) return tipc_cfg_reply_error_string("unsupported argument"); buf = tipc_cfg_reply_alloc(TLV_SPACE(ULTRA_STRING_MAX_LEN)); if (buf == NULL) return NULL; rep_tlv = (struct tlv_desc *)buf->data; pb = TLV_DATA(rep_tlv); pb_len = ULTRA_STRING_MAX_LEN; str_len = tipc_snprintf(pb, pb_len, "TIPC version " TIPC_MOD_VER "\n"); str_len += 1; /* for "\0" */ skb_put(buf, TLV_SPACE(str_len)); TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); return buf; }
static struct sk_buff *tipc_show_stats(void) { struct sk_buff *buf; struct tlv_desc *rep_tlv; struct print_buf pb; int str_len; u32 value; if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); value = ntohl(*(u32 *)TLV_DATA(req_tlv_area)); if (value != 0) return tipc_cfg_reply_error_string("unsupported argument"); buf = tipc_cfg_reply_alloc(TLV_SPACE(MAX_STATS_INFO)); if (buf == NULL) return NULL; rep_tlv = (struct tlv_desc *)buf->data; tipc_printbuf_init(&pb, (char *)TLV_DATA(rep_tlv), MAX_STATS_INFO); tipc_printf(&pb, "TIPC version " TIPC_MOD_VER "\n"); /* Use additional tipc_printf()'s to return more info ... */ str_len = tipc_printbuf_validate(&pb); skb_put(buf, TLV_SPACE(str_len)); TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); return buf; }
struct sk_buff *tipc_log_dump(void) { struct sk_buff *reply; spin_lock_bh(&print_lock); if (!TIPC_LOG->buf) { spin_unlock_bh(&print_lock); reply = tipc_cfg_reply_ultra_string("log not activated\n"); } else if (tipc_printbuf_empty(TIPC_LOG)) { spin_unlock_bh(&print_lock); reply = tipc_cfg_reply_ultra_string("log is empty\n"); } else { struct tlv_desc *rep_tlv; struct print_buf pb; int str_len; str_len = min(TIPC_LOG->size, 32768u); spin_unlock_bh(&print_lock); reply = tipc_cfg_reply_alloc(TLV_SPACE(str_len)); if (reply) { rep_tlv = (struct tlv_desc *)reply->data; tipc_printbuf_init(&pb, TLV_DATA(rep_tlv), str_len); spin_lock_bh(&print_lock); tipc_printbuf_move(&pb, TIPC_LOG); spin_unlock_bh(&print_lock); str_len = strlen(TLV_DATA(rep_tlv)) + 1; skb_put(reply, TLV_SPACE(str_len)); TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); } } return reply; }
static int tipc_add_tlv(struct sk_buff *skb, u16 type, void *data, u16 len) { struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(skb); if (tipc_skb_tailroom(skb) < TLV_SPACE(len)) return -EMSGSIZE; skb_put(skb, TLV_SPACE(len)); tlv->tlv_type = htons(type); tlv->tlv_len = htons(TLV_LENGTH(len)); if (len && data) memcpy(TLV_DATA(tlv), data, len); return 0; }
/** * tipc_bearer_get_names - record names of bearers in buffer */ struct sk_buff *tipc_bearer_get_names(void) { struct sk_buff *buf; struct tipc_bearer *b; int i, j; buf = tipc_cfg_reply_alloc(MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME)); if (!buf) return NULL; read_lock_bh(&tipc_net_lock); for (i = 0; media_info_array[i] != NULL; i++) { for (j = 0; j < MAX_BEARERS; j++) { b = bearer_list[j]; if (!b) continue; if (b->media == media_info_array[i]) { tipc_cfg_append_tlv(buf, TIPC_TLV_BEARER_NAME, b->name, strlen(b->name) + 1); } } } read_unlock_bh(&tipc_net_lock); return buf; }
struct sk_buff *tipc_cfg_reply_string_type(u16 tlv_type, char *string) { struct sk_buff *buf; int string_len = strlen(string) + 1; buf = tipc_cfg_reply_alloc(TLV_SPACE(string_len)); if (buf) tipc_cfg_append_tlv(buf, tlv_type, string, string_len); return buf; }
static struct sk_buff *tipc_get_err_tlv(char *str) { int str_len = strlen(str) + 1; struct sk_buff *buf; buf = tipc_tlv_alloc(TLV_SPACE(str_len)); if (buf) tipc_add_tlv(buf, TIPC_TLV_ERROR_STRING, str, str_len); return buf; }
struct sk_buff *tipc_cfg_reply_unsigned_type(u16 tlv_type, u32 value) { struct sk_buff *buf; __be32 value_net; buf = tipc_cfg_reply_alloc(TLV_SPACE(sizeof(value))); if (buf) { value_net = htonl(value); tipc_cfg_append_tlv(buf, tlv_type, &value_net, sizeof(value_net)); } return buf; }
int tipc_cfg_append_tlv(struct sk_buff *buf, int tlv_type, void *tlv_data, int tlv_data_size) { struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(buf); int new_tlv_space = TLV_SPACE(tlv_data_size); if (skb_tailroom(buf) < new_tlv_space) return 0; skb_put(buf, new_tlv_space); tlv->tlv_type = htons(tlv_type); tlv->tlv_len = htons(TLV_LENGTH(tlv_data_size)); if (tlv_data_size && tlv_data) memcpy(TLV_DATA(tlv), tlv_data, tlv_data_size); return 1; }
/** * tipc_media_get_names - record names of registered media in buffer */ struct sk_buff *tipc_media_get_names(void) { struct sk_buff *buf; int i; buf = tipc_cfg_reply_alloc(MAX_MEDIA * TLV_SPACE(TIPC_MAX_MEDIA_NAME)); if (!buf) return NULL; for (i = 0; media_info_array[i] != NULL; i++) { tipc_cfg_append_tlv(buf, TIPC_TLV_MEDIA_NAME, media_info_array[i]->name, strlen(media_info_array[i]->name) + 1); } return buf; }
static struct sk_buff *tipc_tlv_alloc(int size) { int hdr_len; struct sk_buff *buf; size = TLV_SPACE(size); hdr_len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN); buf = alloc_skb(hdr_len + size, GFP_KERNEL); if (!buf) return NULL; skb_reserve(buf, hdr_len); return buf; }
struct sk_buff *tipc_media_get_names(void) { struct sk_buff *buf; int i; buf = tipc_cfg_reply_alloc(MAX_MEDIA * TLV_SPACE(TIPC_MAX_MEDIA_NAME)); if (!buf) return NULL; read_lock_bh(&tipc_net_lock); for (i = 0; i < media_count; i++) { tipc_cfg_append_tlv(buf, TIPC_TLV_MEDIA_NAME, media_list[i]->name, strlen(media_list[i]->name) + 1); } read_unlock_bh(&tipc_net_lock); return buf; }
struct sk_buff *tipc_bearer_get_names(void) { struct sk_buff *buf; struct tipc_bearer *b_ptr; int i, j; buf = tipc_cfg_reply_alloc(MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME)); if (!buf) return NULL; read_lock_bh(&tipc_net_lock); for (i = 0; i < media_count; i++) { for (j = 0; j < MAX_BEARERS; j++) { b_ptr = &tipc_bearers[j]; if (b_ptr->active && (b_ptr->media == media_list[i])) { tipc_cfg_append_tlv(buf, TIPC_TLV_BEARER_NAME, b_ptr->name, strlen(b_ptr->name) + 1); } } } read_unlock_bh(&tipc_net_lock); return buf; }
struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area, int request_space, int reply_headroom) { struct sk_buff *rep_tlv_buf; rtnl_lock(); /* Save request and reply details in a well-known location */ req_tlv_area = request_area; req_tlv_space = request_space; rep_headroom = reply_headroom; /* Check command authorization */ if (likely(in_own_node(orig_node))) { /* command is permitted */ } else { rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (cannot be done remotely)"); goto exit; } /* Call appropriate processing routine */ switch (cmd) { case TIPC_CMD_NOOP: rep_tlv_buf = tipc_cfg_reply_none(); break; case TIPC_CMD_GET_NODES: rep_tlv_buf = tipc_node_get_nodes(req_tlv_area, req_tlv_space); break; case TIPC_CMD_GET_LINKS: rep_tlv_buf = tipc_node_get_links(req_tlv_area, req_tlv_space); break; case TIPC_CMD_SHOW_LINK_STATS: rep_tlv_buf = tipc_link_cmd_show_stats(req_tlv_area, req_tlv_space); break; case TIPC_CMD_RESET_LINK_STATS: rep_tlv_buf = tipc_link_cmd_reset_stats(req_tlv_area, req_tlv_space); break; case TIPC_CMD_SHOW_NAME_TABLE: rep_tlv_buf = tipc_nametbl_get(req_tlv_area, req_tlv_space); break; case TIPC_CMD_GET_BEARER_NAMES: rep_tlv_buf = tipc_bearer_get_names(); break; case TIPC_CMD_GET_MEDIA_NAMES: rep_tlv_buf = tipc_media_get_names(); break; case TIPC_CMD_SHOW_PORTS: rep_tlv_buf = tipc_sk_socks_show(); break; case TIPC_CMD_SHOW_STATS: rep_tlv_buf = tipc_show_stats(); break; case TIPC_CMD_SET_LINK_TOL: case TIPC_CMD_SET_LINK_PRI: case TIPC_CMD_SET_LINK_WINDOW: rep_tlv_buf = tipc_link_cmd_config(req_tlv_area, req_tlv_space, cmd); break; case TIPC_CMD_ENABLE_BEARER: rep_tlv_buf = cfg_enable_bearer(); break; case TIPC_CMD_DISABLE_BEARER: rep_tlv_buf = cfg_disable_bearer(); break; case TIPC_CMD_SET_NODE_ADDR: rep_tlv_buf = cfg_set_own_addr(); break; case TIPC_CMD_SET_MAX_PORTS: rep_tlv_buf = cfg_set_max_ports(); break; case TIPC_CMD_SET_NETID: rep_tlv_buf = cfg_set_netid(); break; case TIPC_CMD_GET_MAX_PORTS: rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_ports); break; case TIPC_CMD_GET_NETID: rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_net_id); break; case TIPC_CMD_NOT_NET_ADMIN: rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_NET_ADMIN); break; case TIPC_CMD_SET_MAX_ZONES: case TIPC_CMD_GET_MAX_ZONES: case TIPC_CMD_SET_MAX_SLAVES: case TIPC_CMD_GET_MAX_SLAVES: case TIPC_CMD_SET_MAX_CLUSTERS: case TIPC_CMD_GET_MAX_CLUSTERS: case TIPC_CMD_SET_MAX_NODES: case TIPC_CMD_GET_MAX_NODES: case TIPC_CMD_SET_MAX_SUBSCR: case TIPC_CMD_GET_MAX_SUBSCR: case TIPC_CMD_SET_MAX_PUBL: case TIPC_CMD_GET_MAX_PUBL: case TIPC_CMD_SET_LOG_SIZE: case TIPC_CMD_SET_REMOTE_MNG: case TIPC_CMD_GET_REMOTE_MNG: case TIPC_CMD_DUMP_LOG: rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (obsolete command)"); break; default: rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (unknown command)"); break; } WARN_ON(rep_tlv_buf->len > TLV_SPACE(ULTRA_STRING_MAX_LEN)); /* Append an error message if we cannot return all requested data */ if (rep_tlv_buf->len == TLV_SPACE(ULTRA_STRING_MAX_LEN)) { if (*(rep_tlv_buf->data + ULTRA_STRING_MAX_LEN) != '\0') sprintf(rep_tlv_buf->data + rep_tlv_buf->len - sizeof(REPLY_TRUNCATED) - 1, REPLY_TRUNCATED); } /* Return reply buffer */ exit: rtnl_unlock(); return rep_tlv_buf; }
static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg) { struct tipc_nl_compat_cmd_dump dump; struct tipc_nl_compat_cmd_doit doit; memset(&dump, 0, sizeof(dump)); memset(&doit, 0, sizeof(doit)); switch (msg->cmd) { case TIPC_CMD_NOOP: msg->rep = tipc_tlv_alloc(0); if (!msg->rep) return -ENOMEM; return 0; case TIPC_CMD_GET_BEARER_NAMES: msg->rep_size = MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME); dump.dumpit = tipc_nl_bearer_dump; dump.format = tipc_nl_compat_bearer_dump; return tipc_nl_compat_dumpit(&dump, msg); case TIPC_CMD_ENABLE_BEARER: msg->req_type = TIPC_TLV_BEARER_CONFIG; doit.doit = tipc_nl_bearer_enable; doit.transcode = tipc_nl_compat_bearer_enable; return tipc_nl_compat_doit(&doit, msg); case TIPC_CMD_DISABLE_BEARER: msg->req_type = TIPC_TLV_BEARER_NAME; doit.doit = tipc_nl_bearer_disable; doit.transcode = tipc_nl_compat_bearer_disable; return tipc_nl_compat_doit(&doit, msg); case TIPC_CMD_SHOW_LINK_STATS: msg->req_type = TIPC_TLV_LINK_NAME; msg->rep_size = ULTRA_STRING_MAX_LEN; msg->rep_type = TIPC_TLV_ULTRA_STRING; dump.dumpit = tipc_nl_link_dump; dump.format = tipc_nl_compat_link_stat_dump; return tipc_nl_compat_dumpit(&dump, msg); case TIPC_CMD_GET_LINKS: msg->req_type = TIPC_TLV_NET_ADDR; msg->rep_size = ULTRA_STRING_MAX_LEN; dump.dumpit = tipc_nl_link_dump; dump.format = tipc_nl_compat_link_dump; return tipc_nl_compat_dumpit(&dump, msg); case TIPC_CMD_SET_LINK_TOL: case TIPC_CMD_SET_LINK_PRI: case TIPC_CMD_SET_LINK_WINDOW: msg->req_type = TIPC_TLV_LINK_CONFIG; doit.doit = tipc_nl_link_set; doit.transcode = tipc_nl_compat_link_set; return tipc_nl_compat_doit(&doit, msg); case TIPC_CMD_RESET_LINK_STATS: msg->req_type = TIPC_TLV_LINK_NAME; doit.doit = tipc_nl_link_reset_stats; doit.transcode = tipc_nl_compat_link_reset_stats; return tipc_nl_compat_doit(&doit, msg); case TIPC_CMD_SHOW_NAME_TABLE: msg->req_type = TIPC_TLV_NAME_TBL_QUERY; msg->rep_size = ULTRA_STRING_MAX_LEN; msg->rep_type = TIPC_TLV_ULTRA_STRING; dump.header = tipc_nl_compat_name_table_dump_header; dump.dumpit = tipc_nl_name_table_dump; dump.format = tipc_nl_compat_name_table_dump; return tipc_nl_compat_dumpit(&dump, msg); case TIPC_CMD_SHOW_PORTS: msg->rep_size = ULTRA_STRING_MAX_LEN; msg->rep_type = TIPC_TLV_ULTRA_STRING; dump.dumpit = tipc_nl_sk_dump; dump.format = tipc_nl_compat_sk_dump; return tipc_nl_compat_dumpit(&dump, msg); case TIPC_CMD_GET_MEDIA_NAMES: msg->rep_size = MAX_MEDIA * TLV_SPACE(TIPC_MAX_MEDIA_NAME); dump.dumpit = tipc_nl_media_dump; dump.format = tipc_nl_compat_media_dump; return tipc_nl_compat_dumpit(&dump, msg); case TIPC_CMD_GET_NODES: msg->rep_size = ULTRA_STRING_MAX_LEN; dump.dumpit = tipc_nl_node_dump; dump.format = tipc_nl_compat_node_dump; return tipc_nl_compat_dumpit(&dump, msg); case TIPC_CMD_SET_NODE_ADDR: msg->req_type = TIPC_TLV_NET_ADDR; doit.doit = tipc_nl_net_set; doit.transcode = tipc_nl_compat_net_set; return tipc_nl_compat_doit(&doit, msg); case TIPC_CMD_SET_NETID: msg->req_type = TIPC_TLV_UNSIGNED; doit.doit = tipc_nl_net_set; doit.transcode = tipc_nl_compat_net_set; return tipc_nl_compat_doit(&doit, msg); case TIPC_CMD_GET_NETID: msg->rep_size = sizeof(u32); dump.dumpit = tipc_nl_net_dump; dump.format = tipc_nl_compat_net_dump; return tipc_nl_compat_dumpit(&dump, msg); case TIPC_CMD_SHOW_STATS: return tipc_cmd_show_stats_compat(msg); } return -EOPNOTSUPP; }
static inline int TLV_GET_DATA_LEN(struct tlv_desc *tlv) { return TLV_GET_LEN(tlv) - TLV_SPACE(0); }