xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c (revision 7850265edd10f3d79a5e81ffedb25aefffaaa768)
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$
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 
53 TAILQ_HEAD(tp_entries, tp_entry);
54 
55 /*
56  * Free the bridge address list.
57  */
58 static void
59 bridge_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  */
73 static void
74 bridge_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  */
93 static int
94 bridge_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  */
113 static void
114 bridge_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  */
142 static struct tp_entry *
143 bridge_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  */
173 static void
174 bridge_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 
192 static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries);
193 static time_t address_list_age;
194 
195 void
196 bridge_addrs_update_listage(void)
197 {
198 	address_list_age = time(NULL);
199 }
200 
201 void
202 bridge_addrs_fini(void)
203 {
204 	bridge_tpe_free(&tp_entries);
205 }
206 
207 void
208 bridge_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  */
216 static struct tp_entry *
217 bridge_addrs_first(void)
218 {
219 	return (TAILQ_FIRST(&tp_entries));
220 }
221 
222 /*
223  * Find the next address in the list.
224  */
225 static struct tp_entry *
226 bridge_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  */
234 struct tp_entry *
235 bridge_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  */
243 struct tp_entry *
244 bridge_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  */
258 void
259 bridge_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  */
271 struct tp_entry *
272 bridge_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  */
295 struct tp_entry *
296 bridge_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 
313 void
314 bridge_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  */
335 static void
336 bridge_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  */
351 static struct tp_entry *
352 bridge_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  */
372 static struct tp_entry *
373 bridge_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 
396 int
397 op_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 	te = NULL; /* Make the compiler happy. */
412 	switch (op) {
413 	    case SNMP_OP_GET:
414 		if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL)
415 			return (SNMP_ERR_NOSUCHNAME);
416 		break;
417 
418 	    case SNMP_OP_GETNEXT:
419 		if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL)
420 			return (SNMP_ERR_NOSUCHNAME);
421 		bridge_addrs_index_append(&val->var, sub, te);
422 		break;
423 
424 	    case SNMP_OP_SET:
425 		return (SNMP_ERR_NOT_WRITEABLE);
426 
427 	    case SNMP_OP_ROLLBACK:
428 	    case SNMP_OP_COMMIT:
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  */
456 static int
457 bridge_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  */
484 static struct tp_entry *
485 bridge_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  */
514 static struct tp_entry *
515 bridge_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 
546 int
547 op_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 = NULL;
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 		return (SNMP_ERR_NOERROR);
576 	}
577 
578 	ret = SNMP_ERR_NOERROR;
579 	switch (val->var.subs[sub - 1]) {
580 	    case LEAF_begemotBridgeTpFdbAddress:
581 		ret = string_get(val, te->tp_addr, ETHER_ADDR_LEN);
582 		break;
583 	    case LEAF_begemotBridgeTpFdbPort:
584 		val->v.integer = te->port_no;
585 		break;
586 	    case LEAF_begemotBridgeTpFdbStatus:
587 		val->v.integer = te->status;
588 		break;
589 	}
590 
591 	return (ret);
592 }
593