xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c (revision e6bfd18d21b225af6a0ed67ceeaf1293b7b9eba5)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 #define	SNMPTREE_TYPES
53 #include "bridge_tree.h"
54 #include "bridge_snmp.h"
55 
56 TAILQ_HEAD(tp_entries, tp_entry);
57 
58 /*
59  * Free the bridge address list.
60  */
61 static void
62 bridge_tpe_free(struct tp_entries *headp)
63 {
64 	struct tp_entry *t;
65 
66 	while ((t = TAILQ_FIRST(headp)) != NULL) {
67 		TAILQ_REMOVE(headp, t, tp_e);
68 		free(t);
69 	}
70 }
71 
72 /*
73  * Free the bridge address entries from the address list,
74  * for the specified bridge interface only.
75  */
76 static void
77 bridge_tpe_bif_free(struct tp_entries *headp,
78 	struct bridge_if *bif)
79 {
80 	struct tp_entry *tp;
81 
82 	while (bif->f_tpa != NULL && bif->sysindex == bif->f_tpa->sysindex) {
83 		tp = TAILQ_NEXT(bif->f_tpa, tp_e);
84 		TAILQ_REMOVE(headp, bif->f_tpa, tp_e);
85 		free(bif->f_tpa);
86 		bif->f_tpa = tp;
87 	}
88 }
89 
90 /*
91  * Compare two mac addresses.
92  * m1 < m2 : -1
93  * m1 > m2 : +1
94  * m1 = m2 :  0
95  */
96 static int
97 bridge_compare_macs(const uint8_t *m1, const uint8_t *m2)
98 {
99 	int i;
100 
101 	for (i = 0; i < ETHER_ADDR_LEN; i++) {
102 		if (m1[i] < m2[i])
103 			return (-1);
104 		if (m1[i] > m2[i])
105 			return (1);
106 	}
107 
108 	return (0);
109 }
110 
111 /*
112  * Insert an address entry in the bridge address TAILQ starting to search
113  * for its place from the position of the first bridge address for the bridge
114  * interface. Update the first bridge address if necessary.
115  */
116 static void
117 bridge_addrs_insert_at(struct tp_entries *headp,
118 	struct tp_entry *ta, struct tp_entry **f_tpa)
119 {
120 	struct tp_entry *t1;
121 
122 	assert(f_tpa != NULL);
123 
124 	for (t1 = *f_tpa;
125 	    t1 != NULL && ta->sysindex == t1->sysindex;
126 	    t1 = TAILQ_NEXT(t1, tp_e)) {
127 		if (bridge_compare_macs(ta->tp_addr, t1->tp_addr) < 0) {
128 			TAILQ_INSERT_BEFORE(t1, ta, tp_e);
129 			if (*f_tpa == t1)
130 				(*f_tpa) = ta;
131 			return;
132 		}
133 	}
134 
135 	if (t1 == NULL)
136 		TAILQ_INSERT_TAIL(headp, ta, tp_e);
137 	else
138 		TAILQ_INSERT_BEFORE(t1, ta, tp_e);
139 }
140 
141 /*
142  * Find an address entry's position in the address list
143  * according to bridge interface name.
144  */
145 static struct tp_entry *
146 bridge_addrs_find_pos(struct tp_entries *headp, uint32_t b_idx)
147 {
148 	uint32_t t_idx;
149 	struct tp_entry *t1;
150 
151 	if ((t1 = TAILQ_FIRST(headp)) == NULL ||
152 	    bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
153 		return (NULL);
154 
155 	t_idx = t1->sysindex;
156 
157 	for (t1 = TAILQ_NEXT(t1, tp_e); t1 != NULL; t1 = TAILQ_NEXT(t1, tp_e)) {
158 
159 		if (t1->sysindex != t_idx) {
160 			if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
161 				return (TAILQ_PREV(t1, tp_entries, tp_e));
162 			else
163 				t_idx = t1->sysindex;
164 		}
165 	}
166 
167 	if (t1 == NULL)
168 		t1 = TAILQ_LAST(headp, tp_entries);
169 
170 	return (t1);
171 }
172 
173 /*
174  * Insert a bridge address in the bridge addresses list.
175  */
176 static void
177 bridge_addrs_bif_insert(struct tp_entries *headp, struct tp_entry *te,
178     struct tp_entry **f_tpa)
179 {
180 	struct tp_entry *temp;
181 
182 	if (*f_tpa != NULL)
183 		bridge_addrs_insert_at(headp, te, f_tpa);
184 	else {
185 		temp = bridge_addrs_find_pos(headp, te->sysindex);
186 
187 		if (temp == NULL)
188 			TAILQ_INSERT_HEAD(headp, te, tp_e);
189 		else
190 			TAILQ_INSERT_AFTER(headp, temp, te, tp_e);
191 		*f_tpa = te;
192 	}
193 }
194 
195 static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries);
196 static time_t address_list_age;
197 
198 void
199 bridge_addrs_update_listage(void)
200 {
201 	address_list_age = time(NULL);
202 }
203 
204 void
205 bridge_addrs_fini(void)
206 {
207 	bridge_tpe_free(&tp_entries);
208 }
209 
210 void
211 bridge_addrs_free(struct bridge_if *bif)
212 {
213 	bridge_tpe_bif_free(&tp_entries, bif);
214 }
215 
216 /*
217  * Find the first address in the list.
218  */
219 static struct tp_entry *
220 bridge_addrs_first(void)
221 {
222 	return (TAILQ_FIRST(&tp_entries));
223 }
224 
225 /*
226  * Find the next address in the list.
227  */
228 static struct tp_entry *
229 bridge_addrs_next(struct tp_entry *te)
230 {
231 	return (TAILQ_NEXT(te, tp_e));
232 }
233 
234 /*
235  * Find the first address, learnt by the specified bridge interface.
236  */
237 struct tp_entry *
238 bridge_addrs_bif_first(struct bridge_if *bif)
239 {
240 	return (bif->f_tpa);
241 }
242 
243 /*
244  * Find the next address, learnt by the specified bridge interface.
245  */
246 struct tp_entry *
247 bridge_addrs_bif_next(struct tp_entry *te)
248 {
249 	struct tp_entry *te_next;
250 
251 	if ((te_next = TAILQ_NEXT(te, tp_e)) == NULL ||
252 	    te_next->sysindex != te->sysindex)
253 		return (NULL);
254 
255 	return (te_next);
256 }
257 
258 /*
259  * Remove a bridge address from the list.
260  */
261 void
262 bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif)
263 {
264 	if (bif->f_tpa == te)
265 		bif->f_tpa = bridge_addrs_bif_next(te);
266 
267 	TAILQ_REMOVE(&tp_entries, te, tp_e);
268 	free(te);
269 }
270 
271 /*
272  * Allocate memory for a new bridge address and insert it in the list.
273  */
274 struct tp_entry *
275 bridge_new_addrs(uint8_t *mac, struct bridge_if *bif)
276 {
277 	struct tp_entry *te;
278 
279 	if ((te = (struct tp_entry *) malloc(sizeof(*te))) == NULL) {
280 		syslog(LOG_ERR, "bridge new address: failed: %s",
281 		    strerror(errno));
282 		return (NULL);
283 	}
284 
285 	bzero(te, sizeof(*te));
286 
287 	te->sysindex = bif->sysindex;
288 	bcopy(mac, te->tp_addr, ETHER_ADDR_LEN);
289 	bridge_addrs_bif_insert(&tp_entries, te, &(bif->f_tpa));
290 
291 	return (te);
292 }
293 
294 /*
295  * Given a mac address, learnt on a bridge,
296  * find the corrsponding TP entry for it.
297  */
298 struct tp_entry *
299 bridge_addrs_find(uint8_t *mac, struct bridge_if *bif)
300 {
301 	struct tp_entry *te;
302 
303 	for (te = bif->f_tpa; te != NULL; te = TAILQ_NEXT(te, tp_e)) {
304 		if (te->sysindex != bif->sysindex) {
305 			te = NULL;
306 			break;
307 		}
308 
309 		if (bridge_compare_macs(te->tp_addr, mac) == 0)
310 			break;
311 	}
312 
313 	return (te);
314 }
315 
316 void
317 bridge_addrs_dump(struct bridge_if *bif)
318 {
319 	struct tp_entry *te;
320 
321 	syslog(LOG_ERR, "Addresses count - %d", bif->num_addrs);
322 	for (te = bridge_addrs_bif_first(bif); te != NULL;
323 	    te = bridge_addrs_bif_next(te)) {
324 		syslog(LOG_ERR, "address %x:%x:%x:%x:%x:%x on port %d.%d",
325 		    te->tp_addr[0], te->tp_addr[1], te->tp_addr[2],
326 		    te->tp_addr[3], te->tp_addr[4], te->tp_addr[5],
327 		    te->sysindex, te->port_no);
328 	}
329 }
330 
331 /*
332  * RFC4188 specifics.
333  */
334 
335 /*
336  * Construct the SNMP index from the address DST Mac.
337  */
338 static void
339 bridge_addrs_index_append(struct asn_oid *oid, uint sub,
340 	const struct tp_entry *te)
341 {
342 	int i;
343 
344 	oid->len = sub + ETHER_ADDR_LEN + 1;
345 	oid->subs[sub] = ETHER_ADDR_LEN;
346 
347 	for (i = 1; i <= ETHER_ADDR_LEN; i++)
348 		oid->subs[sub + i] = te->tp_addr[i - 1];
349 }
350 
351 /*
352  * Find the address entry for the SNMP index from the default bridge only.
353  */
354 static struct tp_entry *
355 bridge_addrs_get(const struct asn_oid *oid, uint sub,
356 	struct bridge_if *bif)
357 {
358 	int i;
359 	uint8_t tp_addr[ETHER_ADDR_LEN];
360 
361 	if (oid->len - sub != ETHER_ADDR_LEN + 1 ||
362 	    oid->subs[sub] != ETHER_ADDR_LEN)
363 		return (NULL);
364 
365 	for (i = 0; i < ETHER_ADDR_LEN; i++)
366 		tp_addr[i] = oid->subs[sub + i + 1];
367 
368 	return (bridge_addrs_find(tp_addr, bif));
369 }
370 
371 /*
372  * Find the next address entry for the SNMP index
373  * from the default bridge only.
374  */
375 static struct tp_entry *
376 bridge_addrs_getnext(const struct asn_oid *oid, uint sub,
377 	struct bridge_if *bif)
378 {
379 	int i;
380 	uint8_t tp_addr[ETHER_ADDR_LEN];
381 	static struct tp_entry *te;
382 
383 	if (oid->len - sub == 0)
384 		return (bridge_addrs_bif_first(bif));
385 
386 	if (oid->len - sub != ETHER_ADDR_LEN + 1 ||
387 	    oid->subs[sub] != ETHER_ADDR_LEN)
388 		return (NULL);
389 
390 	for (i = 0; i < ETHER_ADDR_LEN; i++)
391 		tp_addr[i] = oid->subs[sub + i + 1];
392 
393 	if ((te = bridge_addrs_find(tp_addr, bif)) == NULL)
394 		return (NULL);
395 
396 	return (bridge_addrs_bif_next(te));
397 }
398 
399 int
400 op_dot1d_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val,
401 	uint sub, uint iidx __unused, enum snmp_op op)
402 {
403 	struct bridge_if *bif;
404 	struct tp_entry *te;
405 
406 	if ((bif = bridge_get_default()) == NULL)
407 		return (SNMP_ERR_NOSUCHNAME);
408 
409 	if (time(NULL) - bif->addrs_age > bridge_get_data_maxage() &&
410 	    bridge_update_addrs(bif) <= 0)
411 		return (SNMP_ERR_NOSUCHNAME);
412 
413 	switch (op) {
414 	    case SNMP_OP_GET:
415 		if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL)
416 			return (SNMP_ERR_NOSUCHNAME);
417 		goto get;
418 
419 	    case SNMP_OP_GETNEXT:
420 		if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL)
421 			return (SNMP_ERR_NOSUCHNAME);
422 		bridge_addrs_index_append(&val->var, sub, te);
423 		goto get;
424 
425 	    case SNMP_OP_SET:
426 		return (SNMP_ERR_NOT_WRITEABLE);
427 
428 	    case SNMP_OP_ROLLBACK:
429 	    case SNMP_OP_COMMIT:
430 		break;
431 	}
432 	abort();
433 
434 get:
435 	switch (val->var.subs[sub - 1]) {
436 		case LEAF_dot1dTpFdbAddress:
437 			return (string_get(val, te->tp_addr, ETHER_ADDR_LEN));
438 		case LEAF_dot1dTpFdbPort :
439 			val->v.integer = te->port_no;
440 			return (SNMP_ERR_NOERROR);
441 		case LEAF_dot1dTpFdbStatus:
442 			val->v.integer = te->status;
443 			return (SNMP_ERR_NOERROR);
444 	}
445 
446 	abort();
447 }
448 
449 /*
450  * Private BEGEMOT-BRIDGE-MIB specifics.
451  */
452 
453 /*
454  * Construct the SNMP index from the bridge interface name
455  * and the address DST Mac.
456  */
457 static int
458 bridge_addrs_begemot_index_append(struct asn_oid *oid, uint sub,
459 	const struct tp_entry *te)
460 {
461 	uint i, n_len;
462 	const char *b_name;
463 
464 	if ((b_name = bridge_if_find_name(te->sysindex)) == NULL)
465 		return (-1);
466 
467 	n_len = strlen(b_name);
468 	oid->len = sub++;
469 	oid->subs[oid->len++] = n_len;
470 
471 	for (i = 1; i <= n_len; i++)
472 		oid->subs[oid->len++] = b_name[i - 1];
473 
474 	oid->subs[oid->len++] = ETHER_ADDR_LEN;
475 	for (i = 1 ; i <= ETHER_ADDR_LEN; i++)
476 		oid->subs[oid->len++] = te->tp_addr[i - 1];
477 
478 	return (0);
479 }
480 
481 /*
482  * Find a bridge address entry by the bridge interface name
483  * and the address DST Mac.
484  */
485 static struct tp_entry *
486 bridge_addrs_begemot_get(const struct asn_oid *oid, uint sub)
487 {
488 	uint i, n_len;
489 	uint8_t tp_addr[ETHER_ADDR_LEN];
490 	char bif_name[IFNAMSIZ];
491 	struct bridge_if *bif;
492 
493 	n_len = oid->subs[sub];
494 	if (oid->len - sub != n_len + ETHER_ADDR_LEN + 3 ||
495 	    n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN)
496 		return (NULL);
497 
498 	for (i = 0; i < n_len; i++)
499 		bif_name[i] = oid->subs[n_len + i + 1];
500 	bif_name[i] = '\0';
501 
502 	for (i = 1; i <= ETHER_ADDR_LEN; i++)
503 		tp_addr[i - 1] = oid->subs[n_len + i + 1];
504 
505 	if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
506 		return (NULL);
507 
508 	return (bridge_addrs_find(tp_addr, bif));
509 }
510 
511 /*
512  * Find the next bridge address entry by the bridge interface name
513  * and the address DST Mac.
514  */
515 static struct tp_entry *
516 bridge_addrs_begemot_getnext(const struct asn_oid *oid, uint sub)
517 {
518 	uint i, n_len;
519 	uint8_t tp_addr[ETHER_ADDR_LEN];
520 	char bif_name[IFNAMSIZ];
521 	struct bridge_if *bif;
522 	struct tp_entry *tp;
523 
524 	if (oid->len - sub == 0)
525 		return (bridge_addrs_first());
526 
527 	n_len = oid->subs[sub];
528 	if (oid->len - sub != n_len + ETHER_ADDR_LEN + 2 ||
529 	    n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN)
530 		return (NULL);
531 
532 	for (i = 1; i <= n_len; i++)
533 		bif_name[i - 1] = oid->subs[sub + i];
534 
535 	bif_name[i - 1] = '\0';
536 
537 	for (i = 1; i <= ETHER_ADDR_LEN; i++)
538 		tp_addr[i - 1] = oid->subs[sub + n_len + i + 1];
539 
540 	if ((bif = bridge_if_find_ifname(bif_name)) == NULL ||
541 	    (tp = bridge_addrs_find(tp_addr, bif)) == NULL)
542 		return (NULL);
543 
544 	return (bridge_addrs_next(tp));
545 }
546 
547 int
548 op_begemot_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val,
549 	uint sub, uint iidx __unused, enum snmp_op op)
550 {
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 		goto get;
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 		goto get;
569 
570 	    case SNMP_OP_SET:
571 		return (SNMP_ERR_NOT_WRITEABLE);
572 
573 	    case SNMP_OP_ROLLBACK:
574 	    case SNMP_OP_COMMIT:
575 		break;
576 	}
577 	abort();
578 
579 get:
580 	switch (val->var.subs[sub - 1]) {
581 	    case LEAF_begemotBridgeTpFdbAddress:
582 		return (string_get(val, te->tp_addr, ETHER_ADDR_LEN));
583 	    case LEAF_begemotBridgeTpFdbPort:
584 		val->v.integer = te->port_no;
585 		return (SNMP_ERR_NOERROR);
586 	    case LEAF_begemotBridgeTpFdbStatus:
587 		val->v.integer = te->status;
588 		return (SNMP_ERR_NOERROR);
589 	}
590 
591 	abort();
592 }
593