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
54TAILQ_HEAD(tp_entries, tp_entry);
55
56/*
57 * Free the bridge address list.
58 */
59static void
60bridge_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 */
74static void
75bridge_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 */
94static int
95bridge_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 */
114static void
115bridge_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 */
143static struct tp_entry *
144bridge_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 */
174static void
175bridge_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
193static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries);
194static time_t address_list_age;
195
196void
197bridge_addrs_update_listage(void)
198{
199	address_list_age = time(NULL);
200}
201
202void
203bridge_addrs_fini(void)
204{
205	bridge_tpe_free(&tp_entries);
206}
207
208void
209bridge_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 */
217static struct tp_entry *
218bridge_addrs_first(void)
219{
220	return (TAILQ_FIRST(&tp_entries));
221}
222
223/*
224 * Find the next address in the list.
225 */
226static struct tp_entry *
227bridge_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 */
235struct tp_entry *
236bridge_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 */
244struct tp_entry *
245bridge_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 */
259void
260bridge_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 */
272struct tp_entry *
273bridge_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 */
296struct tp_entry *
297bridge_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
314void
315bridge_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 */
336static void
337bridge_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 */
352static struct tp_entry *
353bridge_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 */
373static struct tp_entry *
374bridge_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
397int
398op_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
432get:
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 */
455static int
456bridge_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 */
483static struct tp_entry *
484bridge_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 */
513static struct tp_entry *
514bridge_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
545int
546op_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
577get:
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