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