1/*
2 * net/tipc/config.c: TIPC configuration management code
3 *
4 * Copyright (c) 2002-2006, Ericsson AB
5 * Copyright (c) 2004-2006, Wind River Systems
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the names of the copyright holders nor the names of its
17 *    contributors may be used to endorse or promote products derived from
18 *    this software without specific prior written permission.
19 *
20 * Alternatively, this software may be distributed under the terms of the
21 * GNU General Public License ("GPL") version 2 as published by the Free
22 * Software Foundation.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include "core.h"
38#include "dbg.h"
39#include "bearer.h"
40#include "port.h"
41#include "link.h"
42#include "zone.h"
43#include "addr.h"
44#include "name_table.h"
45#include "node.h"
46#include "config.h"
47#include "discover.h"
48
49struct subscr_data {
50	char usr_handle[8];
51	u32 domain;
52	u32 port_ref;
53	struct list_head subd_list;
54};
55
56struct manager {
57	u32 user_ref;
58	u32 port_ref;
59	u32 subscr_ref;
60	u32 link_subscriptions;
61	struct list_head link_subscribers;
62};
63
64static struct manager mng = { 0};
65
66static DEFINE_SPINLOCK(config_lock);
67
68static const void *req_tlv_area;	/* request message TLV area */
69static int req_tlv_space;		/* request message TLV area size */
70static int rep_headroom;		/* reply message headroom to use */
71
72
73void tipc_cfg_link_event(u32 addr, char *name, int up)
74{
75	/* TIPC DOESN'T HANDLE LINK EVENT SUBSCRIPTIONS AT THE MOMENT */
76}
77
78
79struct sk_buff *tipc_cfg_reply_alloc(int payload_size)
80{
81	struct sk_buff *buf;
82
83	buf = alloc_skb(rep_headroom + payload_size, GFP_ATOMIC);
84	if (buf)
85		skb_reserve(buf, rep_headroom);
86	return buf;
87}
88
89int tipc_cfg_append_tlv(struct sk_buff *buf, int tlv_type,
90			void *tlv_data, int tlv_data_size)
91{
92	struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(buf);
93	int new_tlv_space = TLV_SPACE(tlv_data_size);
94
95	if (skb_tailroom(buf) < new_tlv_space) {
96		dbg("tipc_cfg_append_tlv unable to append TLV\n");
97		return 0;
98	}
99	skb_put(buf, new_tlv_space);
100	tlv->tlv_type = htons(tlv_type);
101	tlv->tlv_len  = htons(TLV_LENGTH(tlv_data_size));
102	if (tlv_data_size && tlv_data)
103		memcpy(TLV_DATA(tlv), tlv_data, tlv_data_size);
104	return 1;
105}
106
107struct sk_buff *tipc_cfg_reply_unsigned_type(u16 tlv_type, u32 value)
108{
109	struct sk_buff *buf;
110	__be32 value_net;
111
112	buf = tipc_cfg_reply_alloc(TLV_SPACE(sizeof(value)));
113	if (buf) {
114		value_net = htonl(value);
115		tipc_cfg_append_tlv(buf, tlv_type, &value_net,
116				    sizeof(value_net));
117	}
118	return buf;
119}
120
121struct sk_buff *tipc_cfg_reply_string_type(u16 tlv_type, char *string)
122{
123	struct sk_buff *buf;
124	int string_len = strlen(string) + 1;
125
126	buf = tipc_cfg_reply_alloc(TLV_SPACE(string_len));
127	if (buf)
128		tipc_cfg_append_tlv(buf, tlv_type, string, string_len);
129	return buf;
130}
131
132
133
134
135
136static struct sk_buff *cfg_enable_bearer(void)
137{
138	struct tipc_bearer_config *args;
139
140	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_BEARER_CONFIG))
141		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
142
143	args = (struct tipc_bearer_config *)TLV_DATA(req_tlv_area);
144	if (tipc_enable_bearer(args->name,
145			       ntohl(args->detect_scope),
146			       ntohl(args->priority)))
147		return tipc_cfg_reply_error_string("unable to enable bearer");
148
149	return tipc_cfg_reply_none();
150}
151
152static struct sk_buff *cfg_disable_bearer(void)
153{
154	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_BEARER_NAME))
155		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
156
157	if (tipc_disable_bearer((char *)TLV_DATA(req_tlv_area)))
158		return tipc_cfg_reply_error_string("unable to disable bearer");
159
160	return tipc_cfg_reply_none();
161}
162
163static struct sk_buff *cfg_set_own_addr(void)
164{
165	u32 addr;
166
167	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR))
168		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
169
170	addr = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
171	if (addr == tipc_own_addr)
172		return tipc_cfg_reply_none();
173	if (!tipc_addr_node_valid(addr))
174		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
175						   " (node address)");
176	if (tipc_mode == TIPC_NET_MODE)
177		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
178						   " (cannot change node address once assigned)");
179	tipc_own_addr = addr;
180
181	/*
182	 * Must release all spinlocks before calling start_net() because
183	 * Linux version of TIPC calls eth_media_start() which calls
184	 * register_netdevice_notifier() which may block!
185	 *
186	 * Temporarily releasing the lock should be harmless for non-Linux TIPC,
187	 * but Linux version of eth_media_start() should really be reworked
188	 * so that it can be called with spinlocks held.
189	 */
190
191	spin_unlock_bh(&config_lock);
192	tipc_core_start_net();
193	spin_lock_bh(&config_lock);
194	return tipc_cfg_reply_none();
195}
196
197static struct sk_buff *cfg_set_remote_mng(void)
198{
199	u32 value;
200
201	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
202		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
203
204	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
205	tipc_remote_management = (value != 0);
206	return tipc_cfg_reply_none();
207}
208
209static struct sk_buff *cfg_set_max_publications(void)
210{
211	u32 value;
212
213	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
214		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
215
216	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
217	if (value != delimit(value, 1, 65535))
218		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
219						   " (max publications must be 1-65535)");
220	tipc_max_publications = value;
221	return tipc_cfg_reply_none();
222}
223
224static struct sk_buff *cfg_set_max_subscriptions(void)
225{
226	u32 value;
227
228	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
229		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
230
231	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
232	if (value != delimit(value, 1, 65535))
233		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
234						   " (max subscriptions must be 1-65535");
235	tipc_max_subscriptions = value;
236	return tipc_cfg_reply_none();
237}
238
239static struct sk_buff *cfg_set_max_ports(void)
240{
241	u32 value;
242
243	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
244		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
245	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
246	if (value == tipc_max_ports)
247		return tipc_cfg_reply_none();
248	if (value != delimit(value, 127, 65535))
249		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
250						   " (max ports must be 127-65535)");
251	if (tipc_mode != TIPC_NOT_RUNNING)
252		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
253			" (cannot change max ports while TIPC is active)");
254	tipc_max_ports = value;
255	return tipc_cfg_reply_none();
256}
257
258static struct sk_buff *cfg_set_max_zones(void)
259{
260	u32 value;
261
262	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
263		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
264	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
265	if (value == tipc_max_zones)
266		return tipc_cfg_reply_none();
267	if (value != delimit(value, 1, 255))
268		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
269						   " (max zones must be 1-255)");
270	if (tipc_mode == TIPC_NET_MODE)
271		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
272			" (cannot change max zones once TIPC has joined a network)");
273	tipc_max_zones = value;
274	return tipc_cfg_reply_none();
275}
276
277static struct sk_buff *cfg_set_max_clusters(void)
278{
279	u32 value;
280
281	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
282		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
283	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
284	if (value != delimit(value, 1, 1))
285		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
286						   " (max clusters fixed at 1)");
287	return tipc_cfg_reply_none();
288}
289
290static struct sk_buff *cfg_set_max_nodes(void)
291{
292	u32 value;
293
294	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
295		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
296	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
297	if (value == tipc_max_nodes)
298		return tipc_cfg_reply_none();
299	if (value != delimit(value, 8, 2047))
300		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
301						   " (max nodes must be 8-2047)");
302	if (tipc_mode == TIPC_NET_MODE)
303		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
304			" (cannot change max nodes once TIPC has joined a network)");
305	tipc_max_nodes = value;
306	return tipc_cfg_reply_none();
307}
308
309static struct sk_buff *cfg_set_max_slaves(void)
310{
311	u32 value;
312
313	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
314		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
315	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
316	if (value != 0)
317		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
318						   " (max secondary nodes fixed at 0)");
319	return tipc_cfg_reply_none();
320}
321
322static struct sk_buff *cfg_set_netid(void)
323{
324	u32 value;
325
326	if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
327		return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
328	value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
329	if (value == tipc_net_id)
330		return tipc_cfg_reply_none();
331	if (value != delimit(value, 1, 9999))
332		return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
333						   " (network id must be 1-9999)");
334	if (tipc_mode == TIPC_NET_MODE)
335		return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
336			" (cannot change network id once TIPC has joined a network)");
337	tipc_net_id = value;
338	return tipc_cfg_reply_none();
339}
340
341struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area,
342				int request_space, int reply_headroom)
343{
344	struct sk_buff *rep_tlv_buf;
345
346	spin_lock_bh(&config_lock);
347
348	/* Save request and reply details in a well-known location */
349
350	req_tlv_area = request_area;
351	req_tlv_space = request_space;
352	rep_headroom = reply_headroom;
353
354	/* Check command authorization */
355
356	if (likely(orig_node == tipc_own_addr)) {
357		/* command is permitted */
358	} else if (cmd >= 0x8000) {
359		rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
360							  " (cannot be done remotely)");
361		goto exit;
362	} else if (!tipc_remote_management) {
363		rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NO_REMOTE);
364		goto exit;
365	}
366	else if (cmd >= 0x4000) {
367		u32 domain = 0;
368
369		if ((tipc_nametbl_translate(TIPC_ZM_SRV, 0, &domain) == 0) ||
370		    (domain != orig_node)) {
371			rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_ZONE_MSTR);
372			goto exit;
373		}
374	}
375
376	/* Call appropriate processing routine */
377
378	switch (cmd) {
379	case TIPC_CMD_NOOP:
380		rep_tlv_buf = tipc_cfg_reply_none();
381		break;
382	case TIPC_CMD_GET_NODES:
383		rep_tlv_buf = tipc_node_get_nodes(req_tlv_area, req_tlv_space);
384		break;
385	case TIPC_CMD_GET_LINKS:
386		rep_tlv_buf = tipc_node_get_links(req_tlv_area, req_tlv_space);
387		break;
388	case TIPC_CMD_SHOW_LINK_STATS:
389		rep_tlv_buf = tipc_link_cmd_show_stats(req_tlv_area, req_tlv_space);
390		break;
391	case TIPC_CMD_RESET_LINK_STATS:
392		rep_tlv_buf = tipc_link_cmd_reset_stats(req_tlv_area, req_tlv_space);
393		break;
394	case TIPC_CMD_SHOW_NAME_TABLE:
395		rep_tlv_buf = tipc_nametbl_get(req_tlv_area, req_tlv_space);
396		break;
397	case TIPC_CMD_GET_BEARER_NAMES:
398		rep_tlv_buf = tipc_bearer_get_names();
399		break;
400	case TIPC_CMD_GET_MEDIA_NAMES:
401		rep_tlv_buf = tipc_media_get_names();
402		break;
403	case TIPC_CMD_SHOW_PORTS:
404		rep_tlv_buf = tipc_port_get_ports();
405		break;
406	case TIPC_CMD_SET_LOG_SIZE:
407		rep_tlv_buf = tipc_log_resize(req_tlv_area, req_tlv_space);
408		break;
409	case TIPC_CMD_DUMP_LOG:
410		rep_tlv_buf = tipc_log_dump();
411		break;
412	case TIPC_CMD_SET_LINK_TOL:
413	case TIPC_CMD_SET_LINK_PRI:
414	case TIPC_CMD_SET_LINK_WINDOW:
415		rep_tlv_buf = tipc_link_cmd_config(req_tlv_area, req_tlv_space, cmd);
416		break;
417	case TIPC_CMD_ENABLE_BEARER:
418		rep_tlv_buf = cfg_enable_bearer();
419		break;
420	case TIPC_CMD_DISABLE_BEARER:
421		rep_tlv_buf = cfg_disable_bearer();
422		break;
423	case TIPC_CMD_SET_NODE_ADDR:
424		rep_tlv_buf = cfg_set_own_addr();
425		break;
426	case TIPC_CMD_SET_REMOTE_MNG:
427		rep_tlv_buf = cfg_set_remote_mng();
428		break;
429	case TIPC_CMD_SET_MAX_PORTS:
430		rep_tlv_buf = cfg_set_max_ports();
431		break;
432	case TIPC_CMD_SET_MAX_PUBL:
433		rep_tlv_buf = cfg_set_max_publications();
434		break;
435	case TIPC_CMD_SET_MAX_SUBSCR:
436		rep_tlv_buf = cfg_set_max_subscriptions();
437		break;
438	case TIPC_CMD_SET_MAX_ZONES:
439		rep_tlv_buf = cfg_set_max_zones();
440		break;
441	case TIPC_CMD_SET_MAX_CLUSTERS:
442		rep_tlv_buf = cfg_set_max_clusters();
443		break;
444	case TIPC_CMD_SET_MAX_NODES:
445		rep_tlv_buf = cfg_set_max_nodes();
446		break;
447	case TIPC_CMD_SET_MAX_SLAVES:
448		rep_tlv_buf = cfg_set_max_slaves();
449		break;
450	case TIPC_CMD_SET_NETID:
451		rep_tlv_buf = cfg_set_netid();
452		break;
453	case TIPC_CMD_GET_REMOTE_MNG:
454		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_remote_management);
455		break;
456	case TIPC_CMD_GET_MAX_PORTS:
457		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_ports);
458		break;
459	case TIPC_CMD_GET_MAX_PUBL:
460		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_publications);
461		break;
462	case TIPC_CMD_GET_MAX_SUBSCR:
463		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_subscriptions);
464		break;
465	case TIPC_CMD_GET_MAX_ZONES:
466		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_zones);
467		break;
468	case TIPC_CMD_GET_MAX_CLUSTERS:
469		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_clusters);
470		break;
471	case TIPC_CMD_GET_MAX_NODES:
472		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_nodes);
473		break;
474	case TIPC_CMD_GET_MAX_SLAVES:
475		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_max_slaves);
476		break;
477	case TIPC_CMD_GET_NETID:
478		rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_net_id);
479		break;
480	default:
481		rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
482							  " (unknown command)");
483		break;
484	}
485
486	/* Return reply buffer */
487exit:
488	spin_unlock_bh(&config_lock);
489	return rep_tlv_buf;
490}
491
492static void cfg_named_msg_event(void *userdata,
493				u32 port_ref,
494				struct sk_buff **buf,
495				const unchar *msg,
496				u32 size,
497				u32 importance,
498				struct tipc_portid const *orig,
499				struct tipc_name_seq const *dest)
500{
501	struct tipc_cfg_msg_hdr *req_hdr;
502	struct tipc_cfg_msg_hdr *rep_hdr;
503	struct sk_buff *rep_buf;
504
505	/* Validate configuration message header (ignore invalid message) */
506
507	req_hdr = (struct tipc_cfg_msg_hdr *)msg;
508	if ((size < sizeof(*req_hdr)) ||
509	    (size != TCM_ALIGN(ntohl(req_hdr->tcm_len))) ||
510	    (ntohs(req_hdr->tcm_flags) != TCM_F_REQUEST)) {
511		warn("Invalid configuration message discarded\n");
512		return;
513	}
514
515	/* Generate reply for request (if can't, return request) */
516
517	rep_buf = tipc_cfg_do_cmd(orig->node,
518				  ntohs(req_hdr->tcm_type),
519				  msg + sizeof(*req_hdr),
520				  size - sizeof(*req_hdr),
521				  BUF_HEADROOM + MAX_H_SIZE + sizeof(*rep_hdr));
522	if (rep_buf) {
523		skb_push(rep_buf, sizeof(*rep_hdr));
524		rep_hdr = (struct tipc_cfg_msg_hdr *)rep_buf->data;
525		memcpy(rep_hdr, req_hdr, sizeof(*rep_hdr));
526		rep_hdr->tcm_len = htonl(rep_buf->len);
527		rep_hdr->tcm_flags &= htons(~TCM_F_REQUEST);
528	} else {
529		rep_buf = *buf;
530		*buf = NULL;
531	}
532
533	/* NEED TO ADD CODE TO HANDLE FAILED SEND (SUCH AS CONGESTION) */
534	tipc_send_buf2port(port_ref, orig, rep_buf, rep_buf->len);
535}
536
537int tipc_cfg_init(void)
538{
539	struct tipc_name_seq seq;
540	int res;
541
542	memset(&mng, 0, sizeof(mng));
543	INIT_LIST_HEAD(&mng.link_subscribers);
544
545	res = tipc_attach(&mng.user_ref, NULL, NULL);
546	if (res)
547		goto failed;
548
549	res = tipc_createport(mng.user_ref, NULL, TIPC_CRITICAL_IMPORTANCE,
550			      NULL, NULL, NULL,
551			      NULL, cfg_named_msg_event, NULL,
552			      NULL, &mng.port_ref);
553	if (res)
554		goto failed;
555
556	seq.type = TIPC_CFG_SRV;
557	seq.lower = seq.upper = tipc_own_addr;
558	res = tipc_nametbl_publish_rsv(mng.port_ref, TIPC_ZONE_SCOPE, &seq);
559	if (res)
560		goto failed;
561
562	return 0;
563
564failed:
565	err("Unable to create configuration service\n");
566	tipc_detach(mng.user_ref);
567	mng.user_ref = 0;
568	return res;
569}
570
571void tipc_cfg_stop(void)
572{
573	if (mng.user_ref) {
574		tipc_detach(mng.user_ref);
575		mng.user_ref = 0;
576	}
577}
578