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