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