bearer.c 12.7 KB
Newer Older
Per Liden's avatar
Per Liden committed
1
2
/*
 * net/tipc/bearer.c: TIPC bearer code
3
 *
4
 * Copyright (c) 1996-2006, Ericsson AB
5
 * Copyright (c) 2004-2006, 2010-2011, Wind River Systems
Per Liden's avatar
Per Liden committed
6
7
 * All rights reserved.
 *
Per Liden's avatar
Per Liden committed
8
 * Redistribution and use in source and binary forms, with or without
Per Liden's avatar
Per Liden committed
9
10
 * modification, are permitted provided that the following conditions are met:
 *
Per Liden's avatar
Per Liden committed
11
12
13
14
15
16
17
18
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the names of the copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
Per Liden's avatar
Per Liden committed
19
 *
Per Liden's avatar
Per Liden committed
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
Per Liden's avatar
Per Liden committed
34
35
36
37
38
39
40
41
42
43
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "core.h"
#include "config.h"
#include "bearer.h"
#include "discover.h"

#define MAX_ADDR_STR 32

44
static struct tipc_media *media_list[MAX_MEDIA];
45
static u32 media_count;
Per Liden's avatar
Per Liden committed
46

47
struct tipc_bearer tipc_bearers[MAX_BEARERS];
Per Liden's avatar
Per Liden committed
48

49
50
static void bearer_disable(struct tipc_bearer *b_ptr);

Per Liden's avatar
Per Liden committed
51
/**
52
 * tipc_media_find - locates specified media object by name
Per Liden's avatar
Per Liden committed
53
 */
54
struct tipc_media *tipc_media_find(const char *name)
Per Liden's avatar
Per Liden committed
55
56
57
{
	u32 i;

58
59
60
	for (i = 0; i < media_count; i++) {
		if (!strcmp(media_list[i]->name, name))
			return media_list[i];
Per Liden's avatar
Per Liden committed
61
	}
62
	return NULL;
Per Liden's avatar
Per Liden committed
63
64
}

65
66
67
/**
 * media_find_id - locates specified media object by type identifier
 */
68
static struct tipc_media *media_find_id(u8 type)
69
70
71
72
{
	u32 i;

	for (i = 0; i < media_count; i++) {
73
74
		if (media_list[i]->type_id == type)
			return media_list[i];
75
76
77
78
	}
	return NULL;
}

Per Liden's avatar
Per Liden committed
79
80
/**
 * tipc_register_media - register a media type
81
 *
Per Liden's avatar
Per Liden committed
82
83
 * Bearers for this media type must be activated separately at a later stage.
 */
84
int tipc_register_media(struct tipc_media *m_ptr)
Per Liden's avatar
Per Liden committed
85
86
87
{
	int res = -EINVAL;

88
	write_lock_bh(&tipc_net_lock);
Per Liden's avatar
Per Liden committed
89

90
	if ((strlen(m_ptr->name) + 1) > TIPC_MAX_MEDIA_NAME)
91
		goto exit;
92
	if (m_ptr->priority > TIPC_MAX_LINK_PRI)
Per Liden's avatar
Per Liden committed
93
		goto exit;
94
	if ((m_ptr->tolerance < TIPC_MIN_LINK_TOL) ||
95
	    (m_ptr->tolerance > TIPC_MAX_LINK_TOL))
Per Liden's avatar
Per Liden committed
96
		goto exit;
97
	if (media_count >= MAX_MEDIA)
Per Liden's avatar
Per Liden committed
98
		goto exit;
99
	if (tipc_media_find(m_ptr->name) || media_find_id(m_ptr->type_id))
100
		goto exit;
Per Liden's avatar
Per Liden committed
101

102
	media_list[media_count] = m_ptr;
103
	media_count++;
Per Liden's avatar
Per Liden committed
104
105
	res = 0;
exit:
106
	write_unlock_bh(&tipc_net_lock);
107
	if (res)
108
		pr_warn("Media <%s> registration error\n", m_ptr->name);
Per Liden's avatar
Per Liden committed
109
110
111
112
	return res;
}

/**
113
 * tipc_media_addr_printf - record media address in print buffer
Per Liden's avatar
Per Liden committed
114
 */
115
void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a)
Per Liden's avatar
Per Liden committed
116
{
117
	char addr_str[MAX_ADDR_STR];
118
	struct tipc_media *m_ptr;
119
	int ret;
Per Liden's avatar
Per Liden committed
120

121
	m_ptr = media_find_id(a->media_id);
Per Liden's avatar
Per Liden committed
122

123
	if (m_ptr && !m_ptr->addr2str(a, addr_str, sizeof(addr_str)))
124
		ret = tipc_snprintf(buf, len, "%s(%s)", m_ptr->name, addr_str);
125
126
	else {
		u32 i;
Per Liden's avatar
Per Liden committed
127

128
		ret = tipc_snprintf(buf, len, "UNKNOWN(%u)", a->media_id);
129
		for (i = 0; i < sizeof(a->value); i++)
130
131
			ret += tipc_snprintf(buf - ret, len + ret,
					    "-%02x", a->value[i]);
Per Liden's avatar
Per Liden committed
132
133
134
135
	}
}

/**
136
 * tipc_media_get_names - record names of registered media in buffer
Per Liden's avatar
Per Liden committed
137
 */
138
struct sk_buff *tipc_media_get_names(void)
Per Liden's avatar
Per Liden committed
139
140
141
142
{
	struct sk_buff *buf;
	int i;

143
	buf = tipc_cfg_reply_alloc(MAX_MEDIA * TLV_SPACE(TIPC_MAX_MEDIA_NAME));
Per Liden's avatar
Per Liden committed
144
145
146
	if (!buf)
		return NULL;

147
	read_lock_bh(&tipc_net_lock);
148
149
150
151
	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);
Per Liden's avatar
Per Liden committed
152
	}
153
	read_unlock_bh(&tipc_net_lock);
Per Liden's avatar
Per Liden committed
154
155
156
157
158
	return buf;
}

/**
 * bearer_name_validate - validate & (optionally) deconstruct bearer name
159
160
 * @name: ptr to bearer name string
 * @name_parts: ptr to area for bearer name components (or NULL if not needed)
161
 *
Per Liden's avatar
Per Liden committed
162
163
 * Returns 1 if bearer name is valid, otherwise 0.
 */
164
static int bearer_name_validate(const char *name,
165
				struct tipc_bearer_names *name_parts)
Per Liden's avatar
Per Liden committed
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
{
	char name_copy[TIPC_MAX_BEARER_NAME];
	char *media_name;
	char *if_name;
	u32 media_len;
	u32 if_len;

	/* copy bearer name & ensure length is OK */
	name_copy[TIPC_MAX_BEARER_NAME - 1] = 0;
	/* need above in case non-Posix strncpy() doesn't pad with nulls */
	strncpy(name_copy, name, TIPC_MAX_BEARER_NAME);
	if (name_copy[TIPC_MAX_BEARER_NAME - 1] != 0)
		return 0;

	/* ensure all component parts of bearer name are present */
	media_name = name_copy;
182
183
	if_name = strchr(media_name, ':');
	if (if_name == NULL)
Per Liden's avatar
Per Liden committed
184
185
186
187
188
189
		return 0;
	*(if_name++) = 0;
	media_len = if_name - media_name;
	if_len = strlen(if_name) + 1;

	/* validate component parts of bearer name */
190
	if ((media_len <= 1) || (media_len > TIPC_MAX_MEDIA_NAME) ||
191
	    (if_len <= 1) || (if_len > TIPC_MAX_IF_NAME))
Per Liden's avatar
Per Liden committed
192
193
194
195
196
197
198
199
200
201
202
		return 0;

	/* return bearer name components, if necessary */
	if (name_parts) {
		strcpy(name_parts->media_name, media_name);
		strcpy(name_parts->if_name, if_name);
	}
	return 1;
}

/**
203
 * tipc_bearer_find - locates bearer object with matching bearer name
Per Liden's avatar
Per Liden committed
204
 */
205
struct tipc_bearer *tipc_bearer_find(const char *name)
Per Liden's avatar
Per Liden committed
206
{
207
	struct tipc_bearer *b_ptr;
Per Liden's avatar
Per Liden committed
208
209
	u32 i;

210
	for (i = 0, b_ptr = tipc_bearers; i < MAX_BEARERS; i++, b_ptr++) {
211
		if (b_ptr->active && (!strcmp(b_ptr->name, name)))
Per Liden's avatar
Per Liden committed
212
213
			return b_ptr;
	}
214
	return NULL;
Per Liden's avatar
Per Liden committed
215
216
217
}

/**
218
 * tipc_bearer_find_interface - locates bearer object with matching interface name
Per Liden's avatar
Per Liden committed
219
 */
220
struct tipc_bearer *tipc_bearer_find_interface(const char *if_name)
Per Liden's avatar
Per Liden committed
221
{
222
	struct tipc_bearer *b_ptr;
Per Liden's avatar
Per Liden committed
223
224
225
	char *b_if_name;
	u32 i;

226
	for (i = 0, b_ptr = tipc_bearers; i < MAX_BEARERS; i++, b_ptr++) {
Per Liden's avatar
Per Liden committed
227
228
		if (!b_ptr->active)
			continue;
229
		b_if_name = strchr(b_ptr->name, ':') + 1;
Per Liden's avatar
Per Liden committed
230
231
232
		if (!strcmp(b_if_name, if_name))
			return b_ptr;
	}
233
	return NULL;
Per Liden's avatar
Per Liden committed
234
235
236
}

/**
237
 * tipc_bearer_get_names - record names of bearers in buffer
Per Liden's avatar
Per Liden committed
238
 */
239
struct sk_buff *tipc_bearer_get_names(void)
Per Liden's avatar
Per Liden committed
240
241
{
	struct sk_buff *buf;
242
	struct tipc_bearer *b_ptr;
Per Liden's avatar
Per Liden committed
243
244
	int i, j;

245
	buf = tipc_cfg_reply_alloc(MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME));
Per Liden's avatar
Per Liden committed
246
247
248
	if (!buf)
		return NULL;

249
	read_lock_bh(&tipc_net_lock);
250
	for (i = 0; i < media_count; i++) {
Per Liden's avatar
Per Liden committed
251
		for (j = 0; j < MAX_BEARERS; j++) {
252
			b_ptr = &tipc_bearers[j];
253
			if (b_ptr->active && (b_ptr->media == media_list[i])) {
254
				tipc_cfg_append_tlv(buf, TIPC_TLV_BEARER_NAME,
255
256
						    b_ptr->name,
						    strlen(b_ptr->name) + 1);
Per Liden's avatar
Per Liden committed
257
258
259
			}
		}
	}
260
	read_unlock_bh(&tipc_net_lock);
Per Liden's avatar
Per Liden committed
261
262
263
	return buf;
}

264
void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest)
Per Liden's avatar
Per Liden committed
265
{
266
267
	tipc_nmap_add(&b_ptr->nodes, dest);
	tipc_bcbearer_sort();
268
	tipc_disc_add_dest(b_ptr->link_req);
Per Liden's avatar
Per Liden committed
269
270
}

271
void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest)
Per Liden's avatar
Per Liden committed
272
{
273
274
	tipc_nmap_remove(&b_ptr->nodes, dest);
	tipc_bcbearer_sort();
275
	tipc_disc_remove_dest(b_ptr->link_req);
Per Liden's avatar
Per Liden committed
276
277
278
}

/*
279
 * Interrupt enabling new requests after bearer blocking:
280
 * See bearer_send().
Per Liden's avatar
Per Liden committed
281
 */
282
void tipc_continue(struct tipc_bearer *b)
Per Liden's avatar
Per Liden committed
283
{
284
285
286
	spin_lock_bh(&b->lock);
	b->blocked = 0;
	spin_unlock_bh(&b->lock);
Per Liden's avatar
Per Liden committed
287
288
289
}

/*
290
 * tipc_bearer_blocked - determines if bearer is currently blocked
Per Liden's avatar
Per Liden committed
291
 */
292
int tipc_bearer_blocked(struct tipc_bearer *b)
Per Liden's avatar
Per Liden committed
293
{
294
	int res;
Per Liden's avatar
Per Liden committed
295

296
297
298
	spin_lock_bh(&b->lock);
	res = b->blocked;
	spin_unlock_bh(&b->lock);
Per Liden's avatar
Per Liden committed
299
300
301
302
303
304

	return res;
}

/**
 * tipc_enable_bearer - enable bearer with the given name
305
 */
306
int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
Per Liden's avatar
Per Liden committed
307
{
308
	struct tipc_bearer *b_ptr;
309
	struct tipc_media *m_ptr;
310
	struct tipc_bearer_names b_names;
Per Liden's avatar
Per Liden committed
311
312
313
314
315
316
	char addr_string[16];
	u32 bearer_id;
	u32 with_this_prio;
	u32 i;
	int res = -EINVAL;

317
	if (!tipc_own_addr) {
318
319
		pr_warn("Bearer <%s> rejected, not supported in standalone mode\n",
			name);
Per Liden's avatar
Per Liden committed
320
		return -ENOPROTOOPT;
321
	}
322
	if (!bearer_name_validate(name, &b_names)) {
323
		pr_warn("Bearer <%s> rejected, illegal name\n", name);
324
		return -EINVAL;
325
	}
326
327
328
329
330
	if (tipc_addr_domain_valid(disc_domain) &&
	    (disc_domain != tipc_own_addr)) {
		if (tipc_in_scope(disc_domain, tipc_own_addr)) {
			disc_domain = tipc_own_addr & TIPC_CLUSTER_MASK;
			res = 0;   /* accept any node in own cluster */
331
		} else if (in_own_cluster_exact(disc_domain))
332
333
334
			res = 0;   /* accept specified node in own cluster */
	}
	if (res) {
335
336
		pr_warn("Bearer <%s> rejected, illegal discovery domain\n",
			name);
337
338
		return -EINVAL;
	}
339
	if ((priority > TIPC_MAX_LINK_PRI) &&
340
	    (priority != TIPC_MEDIA_LINK_PRI)) {
341
		pr_warn("Bearer <%s> rejected, illegal priority\n", name);
Per Liden's avatar
Per Liden committed
342
		return -EINVAL;
343
	}
Per Liden's avatar
Per Liden committed
344

345
	write_lock_bh(&tipc_net_lock);
Per Liden's avatar
Per Liden committed
346

347
	m_ptr = tipc_media_find(b_names.media_name);
Per Liden's avatar
Per Liden committed
348
	if (!m_ptr) {
349
350
		pr_warn("Bearer <%s> rejected, media <%s> not registered\n",
			name, b_names.media_name);
351
		goto exit;
Per Liden's avatar
Per Liden committed
352
	}
353
354

	if (priority == TIPC_MEDIA_LINK_PRI)
Per Liden's avatar
Per Liden committed
355
356
357
358
359
360
		priority = m_ptr->priority;

restart:
	bearer_id = MAX_BEARERS;
	with_this_prio = 1;
	for (i = MAX_BEARERS; i-- != 0; ) {
361
		if (!tipc_bearers[i].active) {
Per Liden's avatar
Per Liden committed
362
363
364
			bearer_id = i;
			continue;
		}
365
		if (!strcmp(name, tipc_bearers[i].name)) {
366
367
			pr_warn("Bearer <%s> rejected, already enabled\n",
				name);
368
			goto exit;
Per Liden's avatar
Per Liden committed
369
		}
370
		if ((tipc_bearers[i].priority == priority) &&
Per Liden's avatar
Per Liden committed
371
372
		    (++with_this_prio > 2)) {
			if (priority-- == 0) {
373
374
				pr_warn("Bearer <%s> rejected, duplicate priority\n",
					name);
375
				goto exit;
Per Liden's avatar
Per Liden committed
376
			}
377
378
			pr_warn("Bearer <%s> priority adjustment required %u->%u\n",
				name, priority + 1, priority);
Per Liden's avatar
Per Liden committed
379
380
381
382
			goto restart;
		}
	}
	if (bearer_id >= MAX_BEARERS) {
383
384
		pr_warn("Bearer <%s> rejected, bearer limit reached (%u)\n",
			name, MAX_BEARERS);
385
		goto exit;
Per Liden's avatar
Per Liden committed
386
387
	}

388
	b_ptr = &tipc_bearers[bearer_id];
389
390
	strcpy(b_ptr->name, name);
	res = m_ptr->enable_bearer(b_ptr);
Per Liden's avatar
Per Liden committed
391
	if (res) {
392
393
		pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
			name, -res);
394
		goto exit;
Per Liden's avatar
Per Liden committed
395
396
397
398
	}

	b_ptr->identity = bearer_id;
	b_ptr->media = m_ptr;
399
400
	b_ptr->tolerance = m_ptr->tolerance;
	b_ptr->window = m_ptr->window;
Per Liden's avatar
Per Liden committed
401
402
403
404
	b_ptr->net_plane = bearer_id + 'A';
	b_ptr->active = 1;
	b_ptr->priority = priority;
	INIT_LIST_HEAD(&b_ptr->links);
405
	spin_lock_init(&b_ptr->lock);
406

407
	res = tipc_disc_create(b_ptr, &b_ptr->bcast_addr, disc_domain);
408
409
	if (res) {
		bearer_disable(b_ptr);
410
411
		pr_warn("Bearer <%s> rejected, discovery object creation failed\n",
			name);
412
413
		goto exit;
	}
414
415
416
	pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
		name,
		tipc_addr_string_fill(addr_string, disc_domain), priority);
417
exit:
418
	write_unlock_bh(&tipc_net_lock);
Per Liden's avatar
Per Liden committed
419
420
421
422
	return res;
}

/**
423
 * tipc_block_bearer - Block the bearer with the given name, and reset all its links
Per Liden's avatar
Per Liden committed
424
425
426
 */
int tipc_block_bearer(const char *name)
{
427
	struct tipc_bearer *b_ptr = NULL;
428
429
	struct tipc_link *l_ptr;
	struct tipc_link *temp_l_ptr;
Per Liden's avatar
Per Liden committed
430

431
	read_lock_bh(&tipc_net_lock);
432
	b_ptr = tipc_bearer_find(name);
Per Liden's avatar
Per Liden committed
433
	if (!b_ptr) {
434
		pr_warn("Attempt to block unknown bearer <%s>\n", name);
435
		read_unlock_bh(&tipc_net_lock);
Per Liden's avatar
Per Liden committed
436
437
438
		return -EINVAL;
	}

439
	pr_info("Blocking bearer <%s>\n", name);
440
441
	spin_lock_bh(&b_ptr->lock);
	b_ptr->blocked = 1;
Per Liden's avatar
Per Liden committed
442
	list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
443
		struct tipc_node *n_ptr = l_ptr->owner;
Per Liden's avatar
Per Liden committed
444
445

		spin_lock_bh(&n_ptr->lock);
446
		tipc_link_reset(l_ptr);
Per Liden's avatar
Per Liden committed
447
448
		spin_unlock_bh(&n_ptr->lock);
	}
449
	spin_unlock_bh(&b_ptr->lock);
450
	read_unlock_bh(&tipc_net_lock);
451
	return 0;
Per Liden's avatar
Per Liden committed
452
453
454
}

/**
455
 * bearer_disable
456
 *
457
 * Note: This routine assumes caller holds tipc_net_lock.
Per Liden's avatar
Per Liden committed
458
 */
459
static void bearer_disable(struct tipc_bearer *b_ptr)
Per Liden's avatar
Per Liden committed
460
{
461
462
	struct tipc_link *l_ptr;
	struct tipc_link *temp_l_ptr;
Per Liden's avatar
Per Liden committed
463

464
	pr_info("Disabling bearer <%s>\n", b_ptr->name);
465
466
467
	spin_lock_bh(&b_ptr->lock);
	b_ptr->blocked = 1;
	b_ptr->media->disable_bearer(b_ptr);
Per Liden's avatar
Per Liden committed
468
	list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
469
		tipc_link_delete(l_ptr);
Per Liden's avatar
Per Liden committed
470
	}
471
472
	if (b_ptr->link_req)
		tipc_disc_delete(b_ptr->link_req);
473
474
	spin_unlock_bh(&b_ptr->lock);
	memset(b_ptr, 0, sizeof(struct tipc_bearer));
Per Liden's avatar
Per Liden committed
475
476
477
478
}

int tipc_disable_bearer(const char *name)
{
479
	struct tipc_bearer *b_ptr;
Per Liden's avatar
Per Liden committed
480
481
	int res;

482
	write_lock_bh(&tipc_net_lock);
483
	b_ptr = tipc_bearer_find(name);
484
	if (b_ptr == NULL) {
485
		pr_warn("Attempt to disable unknown bearer <%s>\n", name);
486
		res = -EINVAL;
487
488
489
490
	} else {
		bearer_disable(b_ptr);
		res = 0;
	}
491
	write_unlock_bh(&tipc_net_lock);
Per Liden's avatar
Per Liden committed
492
493
494
495
496
	return res;
}



497
void tipc_bearer_stop(void)
Per Liden's avatar
Per Liden committed
498
499
500
501
{
	u32 i;

	for (i = 0; i < MAX_BEARERS; i++) {
502
		if (tipc_bearers[i].active)
503
			bearer_disable(&tipc_bearers[i]);
Per Liden's avatar
Per Liden committed
504
505
506
	}
	media_count = 0;
}