[TIPC] Moved configuration interface into tipc_config.h
[safe/jmp/linux-2.6] / net / tipc / config.c
1 /*
2  * net/tipc/config.c: TIPC configuration management code
3  * 
4  * Copyright (c) 2003-2005, Ericsson Research Canada
5  * Copyright (c) 2004-2005, Wind River Systems
6  * Copyright (c) 2005-2006, Ericsson AB
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without 
10  * modification, are permitted provided that the following conditions are met:
11  *
12  * Redistributions of source code must retain the above copyright notice, this 
13  * list of conditions and the following disclaimer.
14  * Redistributions in binary form must reproduce the above copyright notice, 
15  * this list of conditions and the following disclaimer in the documentation 
16  * and/or other materials provided with the distribution.
17  * Neither the names of the copyright holders nor the names of its 
18  * contributors may be used to endorse or promote products derived from this 
19  * software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include "core.h"
35 #include "dbg.h"
36 #include "bearer.h"
37 #include "port.h"
38 #include "link.h"
39 #include "zone.h"
40 #include "addr.h"
41 #include "name_table.h"
42 #include "node.h"
43 #include "config.h"
44 #include "discover.h"
45
46 struct subscr_data {
47         char usr_handle[8];
48         u32 domain;
49         u32 port_ref;
50         struct list_head subd_list;
51 };
52
53 struct manager {
54         u32 user_ref;
55         u32 port_ref;
56         u32 subscr_ref;
57         u32 link_subscriptions;
58         struct list_head link_subscribers;
59 };
60
61 static struct manager mng = { 0};
62
63 static spinlock_t config_lock = SPIN_LOCK_UNLOCKED;
64
65 static const void *req_tlv_area;        /* request message TLV area */
66 static int req_tlv_space;               /* request message TLV area size */
67 static int rep_headroom;                /* reply message headroom to use */
68
69
70 void cfg_link_event(u32 addr, char *name, int up)
71 {
72         /* TIPC DOESN'T HANDLE LINK EVENT SUBSCRIPTIONS AT THE MOMENT */
73 }
74
75
76 struct sk_buff *cfg_reply_alloc(int payload_size)
77 {
78         struct sk_buff *buf;
79
80         buf = alloc_skb(rep_headroom + payload_size, GFP_ATOMIC);
81         if (buf)
82                 skb_reserve(buf, rep_headroom);
83         return buf;
84 }
85
86 int cfg_append_tlv(struct sk_buff *buf, int tlv_type, 
87                    void *tlv_data, int tlv_data_size)
88 {
89         struct tlv_desc *tlv = (struct tlv_desc *)buf->tail;
90         int new_tlv_space = TLV_SPACE(tlv_data_size);
91
92         if (skb_tailroom(buf) < new_tlv_space) {
93                 dbg("cfg_append_tlv unable to append TLV\n");
94                 return 0;
95         }
96         skb_put(buf, new_tlv_space);
97         tlv->tlv_type = htons(tlv_type);
98         tlv->tlv_len  = htons(TLV_LENGTH(tlv_data_size));
99         if (tlv_data_size && tlv_data)
100                 memcpy(TLV_DATA(tlv), tlv_data, tlv_data_size);
101         return 1;
102 }
103
104 struct sk_buff *cfg_reply_unsigned_type(u16 tlv_type, u32 value)
105 {
106         struct sk_buff *buf;
107         u32 value_net;
108
109         buf = cfg_reply_alloc(TLV_SPACE(sizeof(value)));
110         if (buf) {
111                 value_net = htonl(value);
112                 cfg_append_tlv(buf, tlv_type, &value_net, 
113                                sizeof(value_net));
114         }
115         return buf;
116 }
117
118 struct sk_buff *cfg_reply_string_type(u16 tlv_type, char *string)
119 {
120         struct sk_buff *buf;
121         int string_len = strlen(string) + 1;
122
123         buf = cfg_reply_alloc(TLV_SPACE(string_len));
124         if (buf)
125                 cfg_append_tlv(buf, tlv_type, string, string_len);
126         return buf;
127 }
128
129
130
131
132 #if 0
133
134 /* Now obsolete code for handling commands not yet implemented the new way */
135
136 int tipc_cfg_cmd(const struct tipc_cmd_msg * msg,
137                  char *data,
138                  u32 sz,
139                  u32 *ret_size,
140                  struct tipc_portid *orig)
141 {
142         int rv = -EINVAL;
143         u32 cmd = msg->cmd;
144
145         *ret_size = 0;
146         switch (cmd) {
147         case TIPC_REMOVE_LINK:
148         case TIPC_CMD_BLOCK_LINK:
149         case TIPC_CMD_UNBLOCK_LINK:
150                 if (!cfg_check_connection(orig))
151                         rv = link_control(msg->argv.link_name, msg->cmd, 0);
152                 break;
153         case TIPC_ESTABLISH:
154                 {
155                         int connected;
156
157                         tipc_isconnected(mng.conn_port_ref, &connected);
158                         if (connected || !orig) {
159                                 rv = TIPC_FAILURE;
160                                 break;
161                         }
162                         rv = tipc_connect2port(mng.conn_port_ref, orig);
163                         if (rv == TIPC_OK)
164                                 orig = 0;
165                         break;
166                 }
167         case TIPC_GET_PEER_ADDRESS:
168                 *ret_size = link_peer_addr(msg->argv.link_name, data, sz);
169                 break;
170         case TIPC_GET_ROUTES:
171                 rv = TIPC_OK;
172                 break;
173         default: {}
174         }
175         if (*ret_size)
176                 rv = TIPC_OK;
177         return rv;
178 }
179
180 static void cfg_cmd_event(struct tipc_cmd_msg *msg,
181                           char *data,
182                           u32 sz,        
183                           struct tipc_portid const *orig)
184 {
185         int rv = -EINVAL;
186         struct tipc_cmd_result_msg rmsg;
187         struct iovec msg_sect[2];
188         int *arg;
189
190         msg->cmd = ntohl(msg->cmd);
191
192         cfg_prepare_res_msg(msg->cmd, msg->usr_handle, rv, &rmsg, msg_sect, 
193                             data, 0);
194         if (ntohl(msg->magic) != TIPC_MAGIC)
195                 goto exit;
196
197         switch (msg->cmd) {
198         case TIPC_CREATE_LINK:
199                 if (!cfg_check_connection(orig))
200                         rv = disc_create_link(&msg->argv.create_link);
201                 break;
202         case TIPC_LINK_SUBSCRIBE:
203                 {
204                         struct subscr_data *sub;
205
206                         if (mng.link_subscriptions > 64)
207                                 break;
208                         sub = (struct subscr_data *)kmalloc(sizeof(*sub),
209                                                             GFP_ATOMIC);
210                         if (sub == NULL) {
211                                 warn("Memory squeeze; dropped remote link subscription\n");
212                                 break;
213                         }
214                         INIT_LIST_HEAD(&sub->subd_list);
215                         tipc_createport(mng.user_ref,
216                                         (void *)sub,
217                                         TIPC_HIGH_IMPORTANCE,
218                                         0,
219                                         0,
220                                         (tipc_conn_shutdown_event)cfg_linksubscr_cancel,
221                                         0,
222                                         0,
223                                         (tipc_conn_msg_event)cfg_linksubscr_cancel,
224                                         0,
225                                         &sub->port_ref);
226                         if (!sub->port_ref) {
227                                 kfree(sub);
228                                 break;
229                         }
230                         memcpy(sub->usr_handle,msg->usr_handle,
231                                sizeof(sub->usr_handle));
232                         sub->domain = msg->argv.domain;
233                         list_add_tail(&sub->subd_list, &mng.link_subscribers);
234                         tipc_connect2port(sub->port_ref, orig);
235                         rmsg.retval = TIPC_OK;
236                         tipc_send(sub->port_ref, 2u, msg_sect);
237                         mng.link_subscriptions++;
238                         return;
239                 }
240         default:
241                 rv = tipc_cfg_cmd(msg, data, sz, (u32 *)&msg_sect[1].iov_len, orig);
242         }
243         exit:
244         rmsg.result_len = htonl(msg_sect[1].iov_len);
245         rmsg.retval = htonl(rv);
246         cfg_respond(msg_sect, 2u, orig);
247 }
248 #endif
249
250 static struct sk_buff *cfg_enable_bearer(void)
251 {
252         struct tipc_bearer_config *args;
253
254         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_BEARER_CONFIG))
255                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
256
257         args = (struct tipc_bearer_config *)TLV_DATA(req_tlv_area);
258         if (tipc_enable_bearer(args->name,
259                                ntohl(args->detect_scope),
260                                ntohl(args->priority)))
261                 return cfg_reply_error_string("unable to enable bearer");
262
263         return cfg_reply_none();
264 }
265
266 static struct sk_buff *cfg_disable_bearer(void)
267 {
268         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_BEARER_NAME))
269                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
270
271         if (tipc_disable_bearer((char *)TLV_DATA(req_tlv_area)))
272                 return cfg_reply_error_string("unable to disable bearer");
273
274         return cfg_reply_none();
275 }
276
277 static struct sk_buff *cfg_set_own_addr(void)
278 {
279         u32 addr;
280
281         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR))
282                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
283
284         addr = *(u32 *)TLV_DATA(req_tlv_area);
285         addr = ntohl(addr);
286         if (addr == tipc_own_addr)
287                 return cfg_reply_none();
288         if (!addr_node_valid(addr))
289                 return cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
290                                               " (node address)");
291         if (tipc_own_addr)
292                 return cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
293                                               " (cannot change node address once assigned)");
294
295         spin_unlock_bh(&config_lock);
296         stop_net();
297         tipc_own_addr = addr;
298         start_net();
299         spin_lock_bh(&config_lock);
300         return cfg_reply_none();
301 }
302
303 static struct sk_buff *cfg_set_remote_mng(void)
304 {
305         u32 value;
306
307         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
308                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
309
310         value = *(u32 *)TLV_DATA(req_tlv_area);
311         value = ntohl(value);
312         tipc_remote_management = (value != 0);
313         return cfg_reply_none();
314 }
315
316 static struct sk_buff *cfg_set_max_publications(void)
317 {
318         u32 value;
319
320         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
321                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
322
323         value = *(u32 *)TLV_DATA(req_tlv_area);
324         value = ntohl(value);
325         if (value != delimit(value, 1, 65535))
326                 return cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
327                                               " (max publications must be 1-65535)");
328         tipc_max_publications = value;
329         return cfg_reply_none();
330 }
331
332 static struct sk_buff *cfg_set_max_subscriptions(void)
333 {
334         u32 value;
335
336         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
337                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
338
339         value = *(u32 *)TLV_DATA(req_tlv_area);
340         value = ntohl(value);
341         if (value != delimit(value, 1, 65535))
342                 return cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
343                                               " (max subscriptions must be 1-65535");
344         tipc_max_subscriptions = value;
345         return cfg_reply_none();
346 }
347
348 static struct sk_buff *cfg_set_max_ports(void)
349 {
350         int orig_mode;
351         u32 value;
352
353         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
354                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
355         value = *(u32 *)TLV_DATA(req_tlv_area);
356         value = ntohl(value);
357         if (value != delimit(value, 127, 65535))
358                 return cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
359                                               " (max ports must be 127-65535)");
360
361         if (value == tipc_max_ports)
362                 return cfg_reply_none();
363
364         if (atomic_read(&tipc_user_count) > 2)
365                 return cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
366                                               " (cannot change max ports while TIPC users exist)");
367
368         spin_unlock_bh(&config_lock);
369         orig_mode = tipc_get_mode();
370         if (orig_mode == TIPC_NET_MODE)
371                 stop_net();
372         stop_core();
373         tipc_max_ports = value;
374         start_core();
375         if (orig_mode == TIPC_NET_MODE)
376                 start_net();
377         spin_lock_bh(&config_lock);
378         return cfg_reply_none();
379 }
380
381 static struct sk_buff *set_net_max(int value, int *parameter)
382 {
383         int orig_mode;
384
385         if (value != *parameter) {
386                 orig_mode = tipc_get_mode();
387                 if (orig_mode == TIPC_NET_MODE)
388                         stop_net();
389                 *parameter = value;
390                 if (orig_mode == TIPC_NET_MODE)
391                         start_net();
392         }
393
394         return cfg_reply_none();
395 }
396
397 static struct sk_buff *cfg_set_max_zones(void)
398 {
399         u32 value;
400
401         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
402                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
403         value = *(u32 *)TLV_DATA(req_tlv_area);
404         value = ntohl(value);
405         if (value != delimit(value, 1, 255))
406                 return cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
407                                               " (max zones must be 1-255)");
408         return set_net_max(value, &tipc_max_zones);
409 }
410
411 static struct sk_buff *cfg_set_max_clusters(void)
412 {
413         u32 value;
414
415         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
416                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
417         value = *(u32 *)TLV_DATA(req_tlv_area);
418         value = ntohl(value);
419         if (value != 1)
420                 return cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
421                                               " (max clusters fixed at 1)");
422         return cfg_reply_none();
423 }
424
425 static struct sk_buff *cfg_set_max_nodes(void)
426 {
427         u32 value;
428
429         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
430                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
431         value = *(u32 *)TLV_DATA(req_tlv_area);
432         value = ntohl(value);
433         if (value != delimit(value, 8, 2047))
434                 return cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
435                                               " (max nodes must be 8-2047)");
436         return set_net_max(value, &tipc_max_nodes);
437 }
438
439 static struct sk_buff *cfg_set_max_slaves(void)
440 {
441         u32 value;
442
443         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
444                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
445         value = *(u32 *)TLV_DATA(req_tlv_area);
446         value = ntohl(value);
447         if (value != 0)
448                 return cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
449                                               " (max secondary nodes fixed at 0)");
450         return cfg_reply_none();
451 }
452
453 static struct sk_buff *cfg_set_netid(void)
454 {
455         u32 value;
456
457         if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED))
458                 return cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
459         value = *(u32 *)TLV_DATA(req_tlv_area);
460         value = ntohl(value);
461         if (value != delimit(value, 1, 9999))
462                 return cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
463                                               " (network id must be 1-9999)");
464
465         if (tipc_own_addr)
466                 return cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
467                                               " (cannot change network id once part of network)");
468         
469         return set_net_max(value, &tipc_net_id);
470 }
471
472 struct sk_buff *cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area,
473                            int request_space, int reply_headroom)
474 {
475         struct sk_buff *rep_tlv_buf;
476
477         spin_lock_bh(&config_lock);
478
479         /* Save request and reply details in a well-known location */
480
481         req_tlv_area = request_area;
482         req_tlv_space = request_space;
483         rep_headroom = reply_headroom;
484
485         /* Check command authorization */
486
487         if (likely(orig_node == tipc_own_addr)) {
488                 /* command is permitted */
489         } else if (cmd >= 0x8000) {
490                 rep_tlv_buf = cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
491                                                      " (cannot be done remotely)");
492                 goto exit;
493         } else if (!tipc_remote_management) {
494                 rep_tlv_buf = cfg_reply_error_string(TIPC_CFG_NO_REMOTE);
495                 goto exit;
496         }
497         else if (cmd >= 0x4000) {
498                 u32 domain = 0;
499
500                 if ((nametbl_translate(TIPC_ZM_SRV, 0, &domain) == 0) ||
501                     (domain != orig_node)) {
502                         rep_tlv_buf = cfg_reply_error_string(TIPC_CFG_NOT_ZONE_MSTR);
503                         goto exit;
504                 }
505         }
506
507         /* Call appropriate processing routine */
508
509         switch (cmd) {
510         case TIPC_CMD_NOOP:
511                 rep_tlv_buf = cfg_reply_none();
512                 break;
513         case TIPC_CMD_GET_NODES:
514                 rep_tlv_buf = node_get_nodes(req_tlv_area, req_tlv_space);
515                 break;
516         case TIPC_CMD_GET_LINKS:
517                 rep_tlv_buf = node_get_links(req_tlv_area, req_tlv_space);
518                 break;
519         case TIPC_CMD_SHOW_LINK_STATS:
520                 rep_tlv_buf = link_cmd_show_stats(req_tlv_area, req_tlv_space);
521                 break;
522         case TIPC_CMD_RESET_LINK_STATS:
523                 rep_tlv_buf = link_cmd_reset_stats(req_tlv_area, req_tlv_space);
524                 break;
525         case TIPC_CMD_SHOW_NAME_TABLE:
526                 rep_tlv_buf = nametbl_get(req_tlv_area, req_tlv_space);
527                 break;
528         case TIPC_CMD_GET_BEARER_NAMES:
529                 rep_tlv_buf = bearer_get_names();
530                 break;
531         case TIPC_CMD_GET_MEDIA_NAMES:
532                 rep_tlv_buf = media_get_names();
533                 break;
534         case TIPC_CMD_SHOW_PORTS:
535                 rep_tlv_buf = port_get_ports();
536                 break;
537 #if 0
538         case TIPC_CMD_SHOW_PORT_STATS:
539                 rep_tlv_buf = port_show_stats(req_tlv_area, req_tlv_space);
540                 break;
541         case TIPC_CMD_RESET_PORT_STATS:
542                 rep_tlv_buf = cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED);
543                 break;
544 #endif
545         case TIPC_CMD_SET_LOG_SIZE:
546                 rep_tlv_buf = log_resize(req_tlv_area, req_tlv_space);
547                 break;
548         case TIPC_CMD_DUMP_LOG:
549                 rep_tlv_buf = log_dump();
550                 break;
551         case TIPC_CMD_SET_LINK_TOL:
552         case TIPC_CMD_SET_LINK_PRI:
553         case TIPC_CMD_SET_LINK_WINDOW:
554                 rep_tlv_buf = link_cmd_config(req_tlv_area, req_tlv_space, cmd);
555                 break;
556         case TIPC_CMD_ENABLE_BEARER:
557                 rep_tlv_buf = cfg_enable_bearer();
558                 break;
559         case TIPC_CMD_DISABLE_BEARER:
560                 rep_tlv_buf = cfg_disable_bearer();
561                 break;
562         case TIPC_CMD_SET_NODE_ADDR:
563                 rep_tlv_buf = cfg_set_own_addr();
564                 break;
565         case TIPC_CMD_SET_REMOTE_MNG:
566                 rep_tlv_buf = cfg_set_remote_mng();
567                 break;
568         case TIPC_CMD_SET_MAX_PORTS:
569                 rep_tlv_buf = cfg_set_max_ports();
570                 break;
571         case TIPC_CMD_SET_MAX_PUBL:
572                 rep_tlv_buf = cfg_set_max_publications();
573                 break;
574         case TIPC_CMD_SET_MAX_SUBSCR:
575                 rep_tlv_buf = cfg_set_max_subscriptions();
576                 break;
577         case TIPC_CMD_SET_MAX_ZONES:
578                 rep_tlv_buf = cfg_set_max_zones();
579                 break;
580         case TIPC_CMD_SET_MAX_CLUSTERS:
581                 rep_tlv_buf = cfg_set_max_clusters();
582                 break;
583         case TIPC_CMD_SET_MAX_NODES:
584                 rep_tlv_buf = cfg_set_max_nodes();
585                 break;
586         case TIPC_CMD_SET_MAX_SLAVES:
587                 rep_tlv_buf = cfg_set_max_slaves();
588                 break;
589         case TIPC_CMD_SET_NETID:
590                 rep_tlv_buf = cfg_set_netid();
591                 break;
592         case TIPC_CMD_GET_REMOTE_MNG:
593                 rep_tlv_buf = cfg_reply_unsigned(tipc_remote_management);
594                 break;
595         case TIPC_CMD_GET_MAX_PORTS:
596                 rep_tlv_buf = cfg_reply_unsigned(tipc_max_ports);
597                 break;
598         case TIPC_CMD_GET_MAX_PUBL:
599                 rep_tlv_buf = cfg_reply_unsigned(tipc_max_publications);
600                 break;
601         case TIPC_CMD_GET_MAX_SUBSCR:
602                 rep_tlv_buf = cfg_reply_unsigned(tipc_max_subscriptions);
603                 break;
604         case TIPC_CMD_GET_MAX_ZONES:
605                 rep_tlv_buf = cfg_reply_unsigned(tipc_max_zones);
606                 break;
607         case TIPC_CMD_GET_MAX_CLUSTERS:
608                 rep_tlv_buf = cfg_reply_unsigned(tipc_max_clusters);
609                 break;
610         case TIPC_CMD_GET_MAX_NODES:
611                 rep_tlv_buf = cfg_reply_unsigned(tipc_max_nodes);
612                 break;
613         case TIPC_CMD_GET_MAX_SLAVES:
614                 rep_tlv_buf = cfg_reply_unsigned(tipc_max_slaves);
615                 break;
616         case TIPC_CMD_GET_NETID:
617                 rep_tlv_buf = cfg_reply_unsigned(tipc_net_id);
618                 break;
619         default:
620                 rep_tlv_buf = NULL;
621                 break;
622         }
623
624         /* Return reply buffer */
625 exit:
626         spin_unlock_bh(&config_lock);
627         return rep_tlv_buf;
628 }
629
630 static void cfg_named_msg_event(void *userdata,
631                                 u32 port_ref,
632                                 struct sk_buff **buf,
633                                 const unchar *msg,
634                                 u32 size,
635                                 u32 importance, 
636                                 struct tipc_portid const *orig,
637                                 struct tipc_name_seq const *dest)
638 {
639         struct tipc_cfg_msg_hdr *req_hdr;
640         struct tipc_cfg_msg_hdr *rep_hdr;
641         struct sk_buff *rep_buf;
642
643         /* Validate configuration message header (ignore invalid message) */
644
645         req_hdr = (struct tipc_cfg_msg_hdr *)msg;
646         if ((size < sizeof(*req_hdr)) ||
647             (size != TCM_ALIGN(ntohl(req_hdr->tcm_len))) ||
648             (ntohs(req_hdr->tcm_flags) != TCM_F_REQUEST)) {
649                 warn("discarded invalid configuration message\n");
650                 return;
651         }
652
653         /* Generate reply for request (if can't, return request) */
654
655         rep_buf = cfg_do_cmd(orig->node,
656                              ntohs(req_hdr->tcm_type), 
657                              msg + sizeof(*req_hdr),
658                              size - sizeof(*req_hdr),
659                              BUF_HEADROOM + MAX_H_SIZE + sizeof(*rep_hdr));
660         if (rep_buf) {
661                 skb_push(rep_buf, sizeof(*rep_hdr));
662                 rep_hdr = (struct tipc_cfg_msg_hdr *)rep_buf->data;
663                 memcpy(rep_hdr, req_hdr, sizeof(*rep_hdr));
664                 rep_hdr->tcm_len = htonl(rep_buf->len);
665                 rep_hdr->tcm_flags &= htons(~TCM_F_REQUEST);
666         } else {
667                 rep_buf = *buf;
668                 *buf = NULL;
669         }
670
671         /* NEED TO ADD CODE TO HANDLE FAILED SEND (SUCH AS CONGESTION) */
672         tipc_send_buf2port(port_ref, orig, rep_buf, rep_buf->len);
673 }
674
675 int cfg_init(void)
676 {
677         struct tipc_name_seq seq;
678         int res;
679
680         memset(&mng, 0, sizeof(mng));
681         INIT_LIST_HEAD(&mng.link_subscribers);
682
683         res = tipc_attach(&mng.user_ref, 0, 0);
684         if (res)
685                 goto failed;
686
687         res = tipc_createport(mng.user_ref, 0, TIPC_CRITICAL_IMPORTANCE,
688                               NULL, NULL, NULL,
689                               NULL, cfg_named_msg_event, NULL,
690                               NULL, &mng.port_ref);
691         if (res)
692                 goto failed;
693
694         seq.type = TIPC_CFG_SRV;
695         seq.lower = seq.upper = tipc_own_addr;
696         res = nametbl_publish_rsv(mng.port_ref, TIPC_ZONE_SCOPE, &seq);
697         if (res)
698                 goto failed;
699
700         return 0;
701
702 failed:
703         err("Unable to create configuration service\n");
704         tipc_detach(mng.user_ref);
705         mng.user_ref = 0;
706         return res;
707 }
708
709 void cfg_stop(void)
710 {
711         if (mng.user_ref) {
712                 tipc_detach(mng.user_ref);
713                 mng.user_ref = 0;
714         }
715 }