bridge_addrs.c revision 165046
1/*-
2 * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * Bridge MIB implementation for SNMPd.
27 * Bridge addresses.
28 *
29 * $FreeBSD: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c 165046 2006-12-09 20:58:26Z syrinx $
30 */
31
32#include <sys/queue.h>
33#include <sys/socket.h>
34#include <sys/types.h>
35
36#include <net/ethernet.h>
37#include <net/if.h>
38#include <net/if_mib.h>
39
40#include <assert.h>
41#include <errno.h>
42#include <stdarg.h>
43#include <string.h>
44#include <stdlib.h>
45#include <syslog.h>
46
47#include <bsnmp/snmpmod.h>
48#include <bsnmp/snmp_mibII.h>
49
50#include "bridge_tree.h"
51#include "bridge_snmp.h"
52
53TAILQ_HEAD(tp_entries, tp_entry);
54
55/*
56 * Free the bridge address list.
57 */
58static void
59bridge_tpe_free(struct tp_entries *headp)
60{
61	struct tp_entry *t;
62
63	while ((t = TAILQ_FIRST(headp)) != NULL) {
64		TAILQ_REMOVE(headp, t, tp_e);
65		free(t);
66	}
67}
68
69/*
70 * Free the bridge address entries from the address list,
71 * for the specified bridge interface only.
72 */
73static void
74bridge_tpe_bif_free(struct tp_entries *headp,
75	struct bridge_if *bif)
76{
77	struct tp_entry *tp;
78
79	while (bif->f_tpa != NULL && bif->sysindex == bif->f_tpa->sysindex) {
80		tp = TAILQ_NEXT(bif->f_tpa, tp_e);
81		TAILQ_REMOVE(headp, bif->f_tpa, tp_e);
82		free(bif->f_tpa);
83		bif->f_tpa = tp;
84	}
85}
86
87/*
88 * Compare two mac addresses.
89 * m1 < m2 : -1
90 * m1 > m2 : +1
91 * m1 = m2 :  0
92 */
93static int
94bridge_compare_macs(const uint8_t *m1, const uint8_t *m2)
95{
96	int i;
97
98	for (i = 0; i < ETHER_ADDR_LEN; i++) {
99		if (m1[i] < m2[i])
100			return (-1);
101		if (m1[i] > m2[i])
102			return (1);
103	}
104
105	return (0);
106}
107
108/*
109 * Insert an address entry in the bridge address TAILQ starting to search
110 * for its place from the position of the first bridge address for the bridge
111 * interface. Update the first bridge address if neccessary.
112 */
113static void
114bridge_addrs_insert_at(struct tp_entries *headp,
115	struct tp_entry *ta, struct tp_entry **f_tpa)
116{
117	struct tp_entry *t1;
118
119	assert(f_tpa != NULL);
120
121	for (t1 = *f_tpa;
122	    t1 != NULL && ta->sysindex == t1->sysindex;
123	    t1 = TAILQ_NEXT(t1, tp_e)) {
124		if (bridge_compare_macs(ta->tp_addr, t1->tp_addr) < 0) {
125			TAILQ_INSERT_BEFORE(t1, ta, tp_e);
126			if (*f_tpa == t1)
127				(*f_tpa) = ta;
128			return;
129		}
130	}
131
132	if (t1 == NULL)
133		TAILQ_INSERT_TAIL(headp, ta, tp_e);
134	else
135		TAILQ_INSERT_BEFORE(t1, ta, tp_e);
136}
137
138/*
139 * Find an address entry's possition in the address list
140 * according to bridge interface name.
141 */
142static struct tp_entry *
143bridge_addrs_find_pos(struct tp_entries *headp, uint32_t b_idx)
144{
145	uint32_t t_idx;
146	struct tp_entry *t1;
147
148	if ((t1 = TAILQ_FIRST(headp)) == NULL ||
149	    bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
150		return (NULL);
151
152	t_idx = t1->sysindex;
153
154	for (t1 = TAILQ_NEXT(t1, tp_e); t1 != NULL; t1 = TAILQ_NEXT(t1, tp_e)) {
155
156		if (t1->sysindex != t_idx) {
157			if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
158				return (TAILQ_PREV(t1, tp_entries, tp_e));
159			else
160				t_idx = t1->sysindex;
161		}
162	}
163
164	if (t1 == NULL)
165		t1 = TAILQ_LAST(headp, tp_entries);
166
167	return (t1);
168}
169
170/*
171 * Insert a bridge address in the bridge addresses list.
172 */
173static void
174bridge_addrs_bif_insert(struct tp_entries *headp, struct tp_entry *te,
175    struct tp_entry **f_tpa)
176{
177	struct tp_entry *temp;
178
179	if (*f_tpa != NULL)
180		bridge_addrs_insert_at(headp, te, f_tpa);
181	else {
182		temp = bridge_addrs_find_pos(headp, te->sysindex);
183
184		if (temp == NULL)
185			TAILQ_INSERT_HEAD(headp, te, tp_e);
186		else
187			TAILQ_INSERT_AFTER(headp, temp, te, tp_e);
188		*f_tpa = te;
189	}
190}
191
192static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries);
193static time_t address_list_age;
194
195void
196bridge_addrs_update_listage(void)
197{
198	address_list_age = time(NULL);
199}
200
201void
202bridge_addrs_fini(void)
203{
204	bridge_tpe_free(&tp_entries);
205}
206
207void
208bridge_addrs_free(struct bridge_if *bif)
209{
210	bridge_tpe_bif_free(&tp_entries, bif);
211}
212
213/*
214 * Find the first address in the list.
215 */
216static struct tp_entry *
217bridge_addrs_first(void)
218{
219	return (TAILQ_FIRST(&tp_entries));
220}
221
222/*
223 * Find the next address in the list.
224 */
225static struct tp_entry *
226bridge_addrs_next(struct tp_entry *te)
227{
228	return (TAILQ_NEXT(te, tp_e));
229}
230
231/*
232 * Find the first address, learnt by the specified bridge interface.
233 */
234struct tp_entry *
235bridge_addrs_bif_first(struct bridge_if *bif)
236{
237	return (bif->f_tpa);
238}
239
240/*
241 * Find the next address, learnt by the specified bridge interface.
242 */
243struct tp_entry *
244bridge_addrs_bif_next(struct tp_entry *te)
245{
246	struct tp_entry *te_next;
247
248	if ((te_next = TAILQ_NEXT(te, tp_e)) == NULL ||
249	    te_next->sysindex != te->sysindex)
250		return (NULL);
251
252	return (te_next);
253}
254
255/*
256 * Remove a bridge address from the list.
257 */
258void
259bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif)
260{
261	if (bif->f_tpa == te)
262		bif->f_tpa = bridge_addrs_bif_next(te);
263
264	TAILQ_REMOVE(&tp_entries, te, tp_e);
265	free(te);
266}
267
268/*
269 * Allocate memory for a new bridge address and insert it in the list.
270 */
271struct tp_entry *
272bridge_new_addrs(uint8_t *mac, struct bridge_if *bif)
273{
274	struct tp_entry *te;
275
276	if ((te = (struct tp_entry *) malloc(sizeof(*te))) == NULL) {
277		syslog(LOG_ERR, "bridge new address: failed: %s",
278		    strerror(errno));
279		return (NULL);
280	}
281
282	bzero(te, sizeof(*te));
283
284	te->sysindex = bif->sysindex;
285	bcopy(mac, te->tp_addr, ETHER_ADDR_LEN);
286	bridge_addrs_bif_insert(&tp_entries, te, &(bif->f_tpa));
287
288	return (te);
289}
290
291/*
292 * Given a mac address, learnt on a bridge,
293 * find the corrsponding TP entry for it.
294 */
295struct tp_entry *
296bridge_addrs_find(uint8_t *mac, struct bridge_if *bif)
297{
298	struct tp_entry *te;
299
300	for (te = bif->f_tpa; te != NULL; te = TAILQ_NEXT(te, tp_e)) {
301		if (te->sysindex != bif->sysindex) {
302			te = NULL;
303			break;
304		}
305
306		if (bridge_compare_macs(te->tp_addr, mac) == 0)
307			break;
308	}
309
310	return (te);
311}
312
313void
314bridge_addrs_dump(struct bridge_if *bif)
315{
316	struct tp_entry *te;
317
318	syslog(LOG_ERR, "Addresses count - %d", bif->num_addrs);
319	for (te = bridge_addrs_bif_first(bif); te != NULL;
320	    te = bridge_addrs_bif_next(te)) {
321		syslog(LOG_ERR, "address %x:%x:%x:%x:%x:%x on port %d.%d",
322		    te->tp_addr[0], te->tp_addr[1], te->tp_addr[2],
323		    te->tp_addr[3], te->tp_addr[4], te->tp_addr[5],
324		    te->sysindex, te->port_no);
325	}
326}
327
328/*
329 * RFC4188 specifics.
330 */
331
332/*
333 * Construct the SNMP index from the address DST Mac.
334 */
335static void
336bridge_addrs_index_append(struct asn_oid *oid, uint sub,
337	const struct tp_entry *te)
338{
339	int i;
340
341	oid->len = sub + ETHER_ADDR_LEN + 1;
342	oid->subs[sub] = ETHER_ADDR_LEN;
343
344	for (i = 1; i <= ETHER_ADDR_LEN; i++)
345		oid->subs[sub + i] = te->tp_addr[i - 1];
346}
347
348/*
349 * Find the address entry for the SNMP index from the default bridge only.
350 */
351static struct tp_entry *
352bridge_addrs_get(const struct asn_oid *oid, uint sub,
353	struct bridge_if *bif)
354{
355	int i;
356	uint8_t tp_addr[ETHER_ADDR_LEN];
357
358	if (oid->len - sub != ETHER_ADDR_LEN + 1 ||
359	    oid->subs[sub] != ETHER_ADDR_LEN)
360		return (NULL);
361
362	for (i = 0; i < ETHER_ADDR_LEN; i++)
363		tp_addr[i] = oid->subs[sub + i + 1];
364
365	return (bridge_addrs_find(tp_addr, bif));
366}
367
368/*
369 * Find the next address entry for the SNMP index
370 * from the default bridge only.
371 */
372static struct tp_entry *
373bridge_addrs_getnext(const struct asn_oid *oid, uint sub,
374	struct bridge_if *bif)
375{
376	int i;
377	uint8_t tp_addr[ETHER_ADDR_LEN];
378	static struct tp_entry *te;
379
380	if (oid->len - sub == 0)
381		return (bridge_addrs_bif_first(bif));
382
383	if (oid->len - sub != ETHER_ADDR_LEN + 1 ||
384	    oid->subs[sub] != ETHER_ADDR_LEN)
385		return (NULL);
386
387	for (i = 0; i < ETHER_ADDR_LEN; i++)
388		tp_addr[i] = oid->subs[sub + i + 1];
389
390	if ((te = bridge_addrs_find(tp_addr, bif)) == NULL)
391		return (NULL);
392
393	return (bridge_addrs_bif_next(te));
394}
395
396int
397op_dot1d_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val,
398	uint sub, uint iidx __unused, enum snmp_op op)
399{
400	int ret;
401	struct bridge_if *bif;
402	struct tp_entry *te;
403
404	if ((bif = bridge_get_default()) == NULL)
405		return (SNMP_ERR_NOSUCHNAME);
406
407	if (time(NULL) - bif->addrs_age > bridge_get_data_maxage() &&
408	    bridge_update_addrs(bif) <= 0)
409		return (SNMP_ERR_NOSUCHNAME);
410
411	switch (op) {
412	    case SNMP_OP_GET:
413		if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL)
414			return (SNMP_ERR_NOSUCHNAME);
415		break;
416
417	    case SNMP_OP_GETNEXT:
418		if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL)
419			return (SNMP_ERR_NOSUCHNAME);
420		bridge_addrs_index_append(&val->var, sub, te);
421		break;
422
423	    case SNMP_OP_SET:
424		return (SNMP_ERR_NOT_WRITEABLE);
425
426	    case SNMP_OP_ROLLBACK:
427	    case SNMP_OP_COMMIT:
428	    default:
429		abort();
430	}
431
432	ret = SNMP_ERR_NOERROR;
433	switch (val->var.subs[sub - 1]) {
434		case LEAF_dot1dTpFdbAddress:
435			ret = string_get(val, te->tp_addr, ETHER_ADDR_LEN);
436			break;
437		case LEAF_dot1dTpFdbPort :
438			val->v.integer = te->port_no;
439			break;
440		case LEAF_dot1dTpFdbStatus:
441			val->v.integer = te->status;
442			break;
443	}
444
445	return (ret);
446}
447
448/*
449 * Private BEGEMOT-BRIDGE-MIB specifics.
450 */
451
452/*
453 * Construct the SNMP index from the bridge interface name
454 * and the address DST Mac.
455 */
456static int
457bridge_addrs_begemot_index_append(struct asn_oid *oid, uint sub,
458	const struct tp_entry *te)
459{
460	uint i, n_len;
461	const char *b_name;
462
463	if ((b_name = bridge_if_find_name(te->sysindex)) == NULL)
464		return (-1);
465
466	n_len = strlen(b_name);
467	oid->len = sub++;
468	oid->subs[oid->len++] = n_len;
469
470	for (i = 1; i <= n_len; i++)
471		oid->subs[oid->len++] = b_name[i - 1];
472
473	oid->subs[oid->len++] = ETHER_ADDR_LEN;
474	for (i = 1 ; i <= ETHER_ADDR_LEN; i++)
475		oid->subs[oid->len++] = te->tp_addr[i - 1];
476
477	return (0);
478}
479
480/*
481 * Find a bridge address entry by the bridge interface name
482 * and the address DST Mac.
483 */
484static struct tp_entry *
485bridge_addrs_begemot_get(const struct asn_oid *oid, uint sub)
486{
487	uint i, n_len;
488	uint8_t tp_addr[ETHER_ADDR_LEN];
489	char bif_name[IFNAMSIZ];
490	struct bridge_if *bif;
491
492	n_len = oid->subs[sub];
493	if (oid->len - sub != n_len + ETHER_ADDR_LEN + 3 ||
494	    n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN)
495		return (NULL);
496
497	for (i = 0; i < n_len; i++)
498		bif_name[i] = oid->subs[n_len + i + 1];
499	bif_name[i] = '\0';
500
501	for (i = 1; i <= ETHER_ADDR_LEN; i++)
502		tp_addr[i - 1] = oid->subs[n_len + i + 1];
503
504	if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
505		return (NULL);
506
507	return (bridge_addrs_find(tp_addr, bif));
508}
509
510/*
511 * Find the next bridge address entry by the bridge interface name
512 * and the address DST Mac.
513 */
514static struct tp_entry *
515bridge_addrs_begemot_getnext(const struct asn_oid *oid, uint sub)
516{
517	uint i, n_len;
518	uint8_t tp_addr[ETHER_ADDR_LEN];
519	char bif_name[IFNAMSIZ];
520	struct bridge_if *bif;
521	struct tp_entry *tp;
522
523	if (oid->len - sub == 0)
524		return (bridge_addrs_first());
525
526	n_len = oid->subs[sub];
527	if (oid->len - sub != n_len + ETHER_ADDR_LEN + 2 ||
528	    n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN)
529		return (NULL);
530
531	for (i = 1; i <= n_len; i++)
532		bif_name[i - 1] = oid->subs[sub + i];
533
534	bif_name[i - 1] = '\0';
535
536	for (i = 1; i <= ETHER_ADDR_LEN; i++)
537		tp_addr[i - 1] = oid->subs[sub + n_len + i + 1];
538
539	if ((bif = bridge_if_find_ifname(bif_name)) == NULL ||
540	    (tp = bridge_addrs_find(tp_addr, bif)) == NULL)
541		return (NULL);
542
543	return (bridge_addrs_next(tp));
544}
545
546int
547op_begemot_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val,
548	uint sub, uint iidx __unused, enum snmp_op op)
549{
550	int ret;
551	struct tp_entry *te;
552
553	if (time(NULL) - address_list_age > bridge_get_data_maxage())
554		bridge_update_all_addrs();
555
556	switch (op) {
557	    case SNMP_OP_GET:
558		if ((te = bridge_addrs_begemot_get(&val->var, sub)) == NULL)
559		    return (SNMP_ERR_NOSUCHNAME);
560		break;
561
562	    case SNMP_OP_GETNEXT:
563		if ((te = bridge_addrs_begemot_getnext(&val->var,
564		    sub)) == NULL ||
565		    bridge_addrs_begemot_index_append(&val->var,
566		    sub, te) < 0)
567			return (SNMP_ERR_NOSUCHNAME);
568		break;
569
570	    case SNMP_OP_SET:
571		return (SNMP_ERR_NOT_WRITEABLE);
572
573	    case SNMP_OP_ROLLBACK:
574	    case SNMP_OP_COMMIT:
575	    default:
576		abort();
577	}
578
579	ret = SNMP_ERR_NOERROR;
580	switch (val->var.subs[sub - 1]) {
581	    case LEAF_begemotBridgeTpFdbAddress:
582		ret = string_get(val, te->tp_addr, ETHER_ADDR_LEN);
583		break;
584	    case LEAF_begemotBridgeTpFdbPort:
585		val->v.integer = te->port_no;
586		break;
587	    case LEAF_begemotBridgeTpFdbStatus:
588		val->v.integer = te->status;
589		break;
590	}
591
592	return (ret);
593}
594