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