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: stable/11/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c 330449 2018-03-05 07:26:05Z eadler $
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
55TAILQ_HEAD(tp_entries, tp_entry);
56
57/*
58 * Free the bridge address list.
59 */
60static void
61bridge_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 */
75static void
76bridge_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 */
95static int
96bridge_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 */
115static void
116bridge_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 */
144static struct tp_entry *
145bridge_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 */
175static void
176bridge_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
194static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries);
195static time_t address_list_age;
196
197void
198bridge_addrs_update_listage(void)
199{
200	address_list_age = time(NULL);
201}
202
203void
204bridge_addrs_fini(void)
205{
206	bridge_tpe_free(&tp_entries);
207}
208
209void
210bridge_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 */
218static struct tp_entry *
219bridge_addrs_first(void)
220{
221	return (TAILQ_FIRST(&tp_entries));
222}
223
224/*
225 * Find the next address in the list.
226 */
227static struct tp_entry *
228bridge_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 */
236struct tp_entry *
237bridge_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 */
245struct tp_entry *
246bridge_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 */
260void
261bridge_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 */
273struct tp_entry *
274bridge_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 */
297struct tp_entry *
298bridge_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
315void
316bridge_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 */
337static void
338bridge_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 */
353static struct tp_entry *
354bridge_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 */
374static struct tp_entry *
375bridge_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
398int
399op_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
433get:
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 */
456static int
457bridge_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 */
484static struct tp_entry *
485bridge_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 */
514static struct tp_entry *
515bridge_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
546int
547op_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
578get:
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