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#define	SNMPTREE_TYPES
53#include "bridge_tree.h"
54#include "bridge_snmp.h"
55
56TAILQ_HEAD(tp_entries, tp_entry);
57
58/*
59 * Free the bridge address list.
60 */
61static void
62bridge_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 */
76static void
77bridge_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 */
96static int
97bridge_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 */
116static void
117bridge_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 */
145static struct tp_entry *
146bridge_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 */
176static void
177bridge_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
195static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries);
196static time_t address_list_age;
197
198void
199bridge_addrs_update_listage(void)
200{
201	address_list_age = time(NULL);
202}
203
204void
205bridge_addrs_fini(void)
206{
207	bridge_tpe_free(&tp_entries);
208}
209
210void
211bridge_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 */
219static struct tp_entry *
220bridge_addrs_first(void)
221{
222	return (TAILQ_FIRST(&tp_entries));
223}
224
225/*
226 * Find the next address in the list.
227 */
228static struct tp_entry *
229bridge_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 */
237struct tp_entry *
238bridge_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 */
246struct tp_entry *
247bridge_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 */
261void
262bridge_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 */
274struct tp_entry *
275bridge_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 */
298struct tp_entry *
299bridge_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
316void
317bridge_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 */
338static void
339bridge_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 */
354static struct tp_entry *
355bridge_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 */
375static struct tp_entry *
376bridge_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
399int
400op_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
434get:
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 */
457static int
458bridge_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 */
485static struct tp_entry *
486bridge_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 */
515static struct tp_entry *
516bridge_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
547int
548op_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
579get:
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