1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 #include <sys/param.h>
23 #include <sys/sysctl.h>
24 #include <sys/ioctl.h>
26 #include <net/if_types.h>
27 #include <net/if_media.h>
28 #include <net/if_vlan_var.h>
29 #include <net/if_dl.h>
30 #if defined HOST_OS_FREEBSD
31 # include <net/if_bridgevar.h>
32 # include <net/if_lagg.h>
33 #elif defined HOST_OS_OPENBSD
34 # include <net/if_bridge.h>
35 # include <net/if_trunk.h>
39 #define IFDESCRSIZE 64
43 ifbsd_check_wireless(struct lldpd
*cfg
,
44 struct ifaddrs
*ifaddr
,
45 struct interfaces_device
*iface
)
47 struct ifmediareq ifmr
= {};
48 strlcpy(ifmr
.ifm_name
, iface
->name
, sizeof(ifmr
.ifm_name
));
49 if (ioctl(cfg
->g_sock
, SIOCGIFMEDIA
, (caddr_t
)&ifmr
) < 0 ||
50 IFM_TYPE(ifmr
.ifm_current
) != IFM_IEEE80211
)
51 return 0; /* Not wireless either */
52 iface
->type
|= IFACE_WIRELESS_T
| IFACE_PHYSICAL_T
;
57 ifbsd_check_bridge(struct lldpd
*cfg
,
58 struct interfaces_device_list
*interfaces
,
59 struct interfaces_device
*master
)
61 struct ifbreq req
[64];
62 struct ifbifconf bifc
= {
63 .ifbic_len
= sizeof(req
),
67 #if defined HOST_OS_FREEBSD
70 .ifd_len
= sizeof(bifc
),
74 strlcpy(ifd
.ifd_name
, master
->name
, sizeof(ifd
.ifd_name
));
75 if (ioctl(cfg
->g_sock
, SIOCGDRVSPEC
, (caddr_t
)&ifd
) < 0) {
76 log_debug("interfaces",
77 "%s is not a bridge", master
->name
);
80 #elif defined HOST_OS_OPENBSD
81 strlcpy(bifc
.ifbic_name
, master
->name
, sizeof(bifc
.ifbic_name
));
82 if (ioctl(cfg
->g_sock
, SIOCBRDGIFS
, (caddr_t
)&bifc
) < 0) {
83 log_debug("interfaces",
84 "%s is not a bridge", master
->name
);
88 # error Unsupported OS
90 if (bifc
.ifbic_len
>= sizeof(req
)) {
91 log_warnx("interfaces",
92 "%s is a bridge too big. Please, report the problem",
96 for (int i
= 0; i
< bifc
.ifbic_len
/ sizeof(*req
); i
++) {
97 struct interfaces_device
*slave
=
98 interfaces_nametointerface(interfaces
,
101 log_warnx("interfaces",
102 "%s should be bridged to %s but we don't know %s",
103 req
[i
].ifbr_ifsname
, master
->name
, req
[i
].ifbr_ifsname
);
106 log_debug("interfaces",
107 "%s is bridged to %s",
108 slave
->name
, master
->name
);
109 slave
->upper
= master
;
111 master
->type
|= IFACE_BRIDGE_T
;
115 ifbsd_check_bond(struct lldpd
*cfg
,
116 struct interfaces_device_list
*interfaces
,
117 struct interfaces_device
*master
)
119 #if defined HOST_OS_OPENBSD
120 /* OpenBSD is the same as FreeBSD, just lagg->trunk */
121 # define lagg_reqport trunk_reqport
122 # define lagg_reqall trunk_reqall
123 # define SIOCGLAGG SIOCGTRUNK
124 # define LAGG_MAX_PORTS TRUNK_MAX_PORTS
126 struct lagg_reqport rpbuf
[LAGG_MAX_PORTS
];
127 struct lagg_reqall ra
= {
128 .ra_size
= sizeof(rpbuf
),
131 strlcpy(ra
.ra_ifname
, master
->name
, IFNAMSIZ
);
132 if (ioctl(cfg
->g_sock
, SIOCGLAGG
, (caddr_t
)&ra
) < 0) {
133 log_debug("interfaces",
134 "%s is not a bond", master
->name
);
138 for (int i
= 0; i
< ra
.ra_ports
; i
++) {
139 struct interfaces_device
*slave
;
140 slave
= interfaces_nametointerface(interfaces
,
141 rpbuf
[i
].rp_portname
);
143 log_warnx("interfaces",
144 "%s should be enslaved to %s but we don't know %s",
145 rpbuf
[i
].rp_portname
, master
->name
,
146 rpbuf
[i
].rp_portname
);
149 log_debug("interfaces",
150 "%s is enslaved to bond %s",
151 slave
->name
, master
->name
);
152 slave
->upper
= master
;
154 master
->type
|= IFACE_BOND_T
;
158 ifbsd_check_vlan(struct lldpd
*cfg
,
159 struct interfaces_device_list
*interfaces
,
160 struct interfaces_device
*vlan
)
162 struct interfaces_device
*lower
;
163 struct vlanreq vreq
= {};
165 .ifr_data
= (caddr_t
)&vreq
167 strlcpy(ifr
.ifr_name
, vlan
->name
, sizeof(ifr
.ifr_name
));
168 if (ioctl(cfg
->g_sock
, SIOCGETVLAN
, (caddr_t
)&ifr
) < 0) {
169 log_debug("interfaces",
170 "%s is not a VLAN", vlan
->name
);
173 if (strlen(vreq
.vlr_parent
) == 0) {
174 log_debug("interfaces",
175 "%s is a VLAN but has no lower interface",
178 vlan
->type
|= IFACE_VLAN_T
;
181 lower
= interfaces_nametointerface(interfaces
,
184 log_warnx("interfaces",
185 "%s should be a VLAN of %s but %s does not exist",
186 vlan
->name
, vreq
.vlr_parent
, vreq
.vlr_parent
);
189 log_debug("interfaces",
190 "%s is VLAN %d of %s",
191 vlan
->name
, vreq
.vlr_tag
, lower
->name
);
193 vlan
->vlanid
= vreq
.vlr_tag
;
194 vlan
->type
|= IFACE_VLAN_T
;
198 ifbsd_check_physical(struct lldpd
*cfg
,
199 struct interfaces_device_list
*interfaces
,
200 struct interfaces_device
*iface
)
202 if (iface
->type
& (IFACE_VLAN_T
|
203 IFACE_BOND_T
|IFACE_BRIDGE_T
|IFACE_PHYSICAL_T
))
206 if (!(iface
->flags
& (IFF_MULTICAST
|IFF_BROADCAST
))) {
207 log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
211 log_debug("interfaces",
212 "%s is a physical interface",
214 iface
->type
|= IFACE_PHYSICAL_T
;
217 static struct interfaces_device
*
218 ifbsd_extract_device(struct lldpd
*cfg
,
219 struct ifaddrs
*ifaddr
)
221 struct interfaces_device
*iface
= NULL
;
222 struct sockaddr_dl
*saddrdl
= (struct sockaddr_dl
*)ifaddr
->ifa_addr
;
223 if ((saddrdl
->sdl_type
!= IFT_BRIDGE
) &&
224 (saddrdl
->sdl_type
!= IFT_L2VLAN
) &&
225 (saddrdl
->sdl_type
!= IFT_ETHER
)) {
226 log_debug("interfaces", "skip %s: not an ethernet device (%d)",
227 ifaddr
->ifa_name
, saddrdl
->sdl_type
);
230 if ((iface
= calloc(1, sizeof(struct interfaces_device
))) == NULL
) {
231 log_warn("interfaces", "unable to allocate memory for %s",
236 iface
->index
= saddrdl
->sdl_index
;
237 iface
->name
= strdup(ifaddr
->ifa_name
);
238 iface
->flags
= ifaddr
->ifa_flags
;
241 iface
->address
= malloc(ETHER_ADDR_LEN
);
243 memcpy(iface
->address
, LLADDR(saddrdl
), ETHER_ADDR_LEN
);
245 /* Grab description */
246 iface
->alias
= malloc(IFDESCRSIZE
);
248 #ifdef HOST_OS_FREEBSD
250 .ifr_buffer
= { .buffer
= iface
->alias
,
251 .length
= IFDESCRSIZE
}
255 .ifr_data
= (caddr_t
)iface
->alias
258 strlcpy(ifr
.ifr_name
, ifaddr
->ifa_name
, sizeof(ifr
.ifr_name
));
259 if (ioctl(cfg
->g_sock
, SIOCGIFDESCR
, (caddr_t
)&ifr
) < 0) {
265 if (ifbsd_check_wireless(cfg
, ifaddr
, iface
) == -1) {
266 interfaces_free_device(iface
);
274 ifbsd_extract(struct lldpd
*cfg
,
275 struct interfaces_device_list
*interfaces
,
276 struct interfaces_address_list
*addresses
,
277 struct ifaddrs
*ifaddr
)
279 struct interfaces_address
*address
= NULL
;
280 struct interfaces_device
*device
= NULL
;
281 if (!ifaddr
->ifa_name
) return;
282 if (!ifaddr
->ifa_addr
) return;
283 if (!(ifaddr
->ifa_flags
& IFF_UP
)) {
284 log_debug("interfaces",
285 "skip %s: down", ifaddr
->ifa_name
);
288 switch (ifaddr
->ifa_addr
->sa_family
) {
290 log_debug("interfaces",
291 "grabbing information on interface %s",
293 device
= ifbsd_extract_device(cfg
, ifaddr
);
295 TAILQ_INSERT_TAIL(interfaces
, device
, next
);
299 log_debug("interfaces",
300 "got an IP address on %s",
302 address
= malloc(sizeof(struct interfaces_address
));
303 if (address
== NULL
) {
304 log_warn("interfaces",
305 "not enough memory for a new IP address on %s",
309 address
->flags
= ifaddr
->ifa_flags
;
310 address
->index
= if_nametoindex(ifaddr
->ifa_name
);
311 memcpy(&address
->address
,
313 (ifaddr
->ifa_addr
->sa_family
== AF_INET
)?
314 sizeof(struct sockaddr_in
):
315 sizeof(struct sockaddr_in6
));
316 TAILQ_INSERT_TAIL(addresses
, address
, next
);
319 log_debug("interfaces", "unhandled family %d for interface %s",
320 ifaddr
->ifa_addr
->sa_family
,
326 ifbsd_macphy(struct lldpd
*cfg
,
327 struct lldpd_hardware
*hardware
)
330 int media_list
[32] = {};
331 struct ifmediareq ifmr
= {
332 .ifm_ulist
= media_list
,
333 .ifm_count
= sizeof(media_list
) / sizeof(int)
335 struct lldpd_port
*port
= &hardware
->h_lport
;
338 int advertised_ifmedia_to_rfc3636
[][3] = {
340 LLDP_DOT3_LINK_AUTONEG_10BASE_T
,
341 LLDP_DOT3_LINK_AUTONEG_10BASET_FD
},
343 LLDP_DOT3_LINK_AUTONEG_10BASE_T
,
344 LLDP_DOT3_LINK_AUTONEG_10BASET_FD
},
346 LLDP_DOT3_LINK_AUTONEG_100BASE_TX
,
347 LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD
},
349 LLDP_DOT3_LINK_AUTONEG_100BASE_T4
,
350 LLDP_DOT3_LINK_AUTONEG_100BASE_T4
},
352 LLDP_DOT3_LINK_AUTONEG_100BASE_T2
,
353 LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD
},
355 LLDP_DOT3_LINK_AUTONEG_1000BASE_X
,
356 LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD
},
358 LLDP_DOT3_LINK_AUTONEG_1000BASE_X
,
359 LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD
},
361 LLDP_DOT3_LINK_AUTONEG_1000BASE_X
,
362 LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD
},
364 LLDP_DOT3_LINK_AUTONEG_1000BASE_T
,
365 LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD
},
368 int current_ifmedia_to_rfc3636
[][3] = {
370 LLDP_DOT3_MAU_10BASETHD
, LLDP_DOT3_MAU_10BASETFD
},
372 LLDP_DOT3_MAU_10BASETHD
, LLDP_DOT3_MAU_10BASETFD
},
374 LLDP_DOT3_MAU_10BASE2
, LLDP_DOT3_MAU_10BASE2
},
376 LLDP_DOT3_MAU_10BASE5
, LLDP_DOT3_MAU_10BASE5
},
378 LLDP_DOT3_MAU_100BASETXHD
, LLDP_DOT3_MAU_100BASETXFD
},
380 LLDP_DOT3_MAU_100BASEFXHD
, LLDP_DOT3_MAU_100BASEFXFD
},
382 LLDP_DOT3_MAU_100BASET2HD
, LLDP_DOT3_MAU_100BASET2FD
},
384 LLDP_DOT3_MAU_1000BASESXHD
, LLDP_DOT3_MAU_1000BASESXFD
},
386 LLDP_DOT3_MAU_10BASEFLHD
, LLDP_DOT3_MAU_10BASEFLFD
},
388 LLDP_DOT3_MAU_1000BASELXHD
, LLDP_DOT3_MAU_1000BASELXFD
},
390 LLDP_DOT3_MAU_1000BASECXHD
, LLDP_DOT3_MAU_1000BASECXFD
},
392 LLDP_DOT3_MAU_1000BASETHD
, LLDP_DOT3_MAU_1000BASETFD
},
394 LLDP_DOT3_MAU_10GIGBASELR
, LLDP_DOT3_MAU_10GIGBASELR
},
396 LLDP_DOT3_MAU_10GIGBASESR
, LLDP_DOT3_MAU_10GIGBASESR
},
398 LLDP_DOT3_MAU_10GIGBASELX4
, LLDP_DOT3_MAU_10GIGBASELX4
},
402 log_debug("interfaces", "get MAC/phy for %s",
404 strlcpy(ifmr
.ifm_name
, hardware
->h_ifname
, sizeof(ifmr
.ifm_name
));
405 if (ioctl(cfg
->g_sock
, SIOCGIFMEDIA
, (caddr_t
)&ifmr
) < 0) {
406 log_warn("interfaces",
407 "unable to get media information from %s",
411 if (IFM_TYPE(ifmr
.ifm_current
) != IFM_ETHER
) {
412 log_warnx("interfaces",
413 "cannot get media information from %s: not an ethernet device",
417 if ((ifmr
.ifm_status
& IFM_ACTIVE
) == 0) {
418 log_debug("interfaces",
419 "interface %s is now down, skip",
423 if (ifmr
.ifm_count
== 0) {
424 log_warnx("interfaces", "no media information available on %s",
428 port
->p_macphy
.autoneg_support
=
429 port
->p_macphy
.autoneg_enabled
= 0;
430 for (int m
= 0; m
< ifmr
.ifm_count
; m
++) {
431 media
= IFM_SUBTYPE(ifmr
.ifm_ulist
[m
]);
432 duplex
= !!(IFM_OPTIONS(ifmr
.ifm_ulist
[m
]) &
434 if (media
== IFM_AUTO
) {
435 port
->p_macphy
.autoneg_support
= 1;
436 port
->p_macphy
.autoneg_enabled
=
437 (IFM_SUBTYPE(ifmr
.ifm_current
) == IFM_AUTO
);
441 for (int j
= 0; advertised_ifmedia_to_rfc3636
[j
][0]; j
++) {
442 if (advertised_ifmedia_to_rfc3636
[j
][0] == media
) {
443 port
->p_macphy
.autoneg_advertised
|=
444 advertised_ifmedia_to_rfc3636
[j
][1 + duplex
];
450 port
->p_macphy
.mau_type
= 0;
451 media
= IFM_SUBTYPE(ifmr
.ifm_active
);
452 duplex
= !!(IFM_OPTIONS(ifmr
.ifm_active
) & IFM_FDX
);
453 for (int j
= 0; current_ifmedia_to_rfc3636
[j
][0]; j
++) {
454 if (current_ifmedia_to_rfc3636
[j
][0] == media
) {
455 port
->p_macphy
.mau_type
=
456 current_ifmedia_to_rfc3636
[j
][1 + duplex
];
464 size_t len
; /* Total length of the buffer */
465 char data
[0]; /* Data */
469 ifbsd_phys_init(struct lldpd
*cfg
,
470 struct lldpd_hardware
*hardware
)
472 struct bpf_insn filter
[] = { LLDPD_FILTER_F
};
473 struct ifreq ifr
= {};
474 struct bpf_program fprog
= {
476 .bf_len
= sizeof(filter
)/sizeof(struct bpf_insn
)
478 struct bpf_buffer
*buffer
= NULL
;
479 int fd
= -1, enable
, required
;
481 log_debug("interfaces", "initialize ethernet device %s",
483 if ((fd
= priv_iface_init(hardware
->h_ifindex
)) == -1)
485 /* We got a file descriptor to /dev/bpfXXX */
487 /* Set buffer size */
488 required
= ETHER_MAX_LEN
;
489 if (ioctl(fd
, BIOCSBLEN
, (caddr_t
)&required
) < 0) {
490 log_warn("interfaces",
491 "unable to set receive buffer size for BPF on %s",
495 hardware
->h_data
= buffer
=
496 malloc(required
+ sizeof(struct bpf_buffer
));
497 if (buffer
== NULL
) {
498 log_warn("interfaces",
499 "unable to allocate buffer space for BPF on %s",
503 buffer
->len
= required
;
505 /* Bind the interface to BPF device */
506 strlcpy(ifr
.ifr_name
, hardware
->h_ifname
, IFNAMSIZ
);
507 if (ioctl(fd
, BIOCSETIF
, (caddr_t
)&ifr
) < 0) {
508 log_warn("interfaces", "failed to bind interface %s to BPF",
513 /* Disable buffering */
515 if (ioctl(fd
, BIOCIMMEDIATE
, (caddr_t
)&enable
) < 0) {
516 log_warn("interfaces", "unable to disable buffering for %s",
521 /* Let us write the MAC address (raw packet mode) */
523 if (ioctl(fd
, BIOCSHDRCMPLT
, (caddr_t
)&enable
) < 0) {
524 log_warn("interfaces",
525 "unable to set the `header complete` flag for %s",
530 #ifdef HOST_OS_FREEBSD
531 /* We only want to receive incoming packets */
533 if (ioctl(fd
, BIOCSDIRECTION
, (caddr_t
)&enable
) < 0) {
535 enable
= BPF_DIRECTION_IN
;
536 if (ioctl(fd
, BIOCSDIRFILT
, (caddr_t
)&enable
) < 0) {
538 log_warn("interfaces",
539 "unable to set packet direction for BPF filter on %s",
544 /* Install read filter */
545 if (ioctl(fd
, BIOCSETF
, (caddr_t
)&fprog
) < 0) {
546 log_warn("interfaces", "unable to setup BPF filter for %s",
550 /* Install write filter (optional) */
551 if (ioctl(fd
, BIOCSETWF
, (caddr_t
)&fprog
) < 0) {
552 log_info("interfaces", "unable to setup write BPF filter for %s",
556 /* Setup multicast */
557 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 0);
559 hardware
->h_sendfd
= fd
; /* Send */
561 levent_hardware_add_fd(hardware
, fd
); /* Receive */
562 log_debug("interfaces", "interface %s initialized (fd=%d)", hardware
->h_ifname
,
567 if (fd
>= 0) close(fd
);
569 hardware
->h_data
= NULL
;
573 /* Ethernet send/receive through BPF */
575 ifbsd_eth_send(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
,
576 char *buffer
, size_t size
)
578 log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)",
579 hardware
->h_ifname
, hardware
->h_sendfd
);
580 return write(hardware
->h_sendfd
,
585 ifbsd_eth_recv(struct lldpd
*cfg
,
586 struct lldpd_hardware
*hardware
,
587 int fd
, char *buffer
, size_t size
)
590 struct bpf_buffer
*bpfbuf
= hardware
->h_data
;
592 log_debug("interfaces", "receive PDU from ethernet device %s",
595 /* We assume we have only receive one packet (unbuffered mode). Dunno if
596 * this is correct. */
597 if ((n
= read(fd
, bpfbuf
->data
, bpfbuf
->len
)) == -1) {
598 log_warn("interfaces", "error while receiving frame on %s",
600 hardware
->h_rx_discarded_cnt
++;
603 bh
= (struct bpf_hdr
*)bpfbuf
->data
;
604 if (bh
->bh_caplen
< size
)
605 size
= bh
->bh_caplen
;
606 memcpy(buffer
, bpfbuf
->data
+ bh
->bh_hdrlen
, size
);
612 ifbsd_eth_close(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
614 log_debug("interfaces", "close ethernet device %s",
616 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 1);
620 static struct lldpd_ops eth_ops
= {
621 .send
= ifbsd_eth_send
,
622 .recv
= ifbsd_eth_recv
,
623 .cleanup
= ifbsd_eth_close
,
627 interfaces_update(struct lldpd
*cfg
)
629 struct lldpd_hardware
*hardware
;
630 struct interfaces_device
*iface
;
631 struct interfaces_device_list
*interfaces
= NULL
;
632 struct interfaces_address_list
*addresses
= NULL
;
633 struct ifaddrs
*ifaddrs
= NULL
, *ifaddr
;
635 interfaces
= malloc(sizeof(struct interfaces_device_list
));
636 addresses
= malloc(sizeof(struct interfaces_address_list
));
637 if (interfaces
== NULL
|| addresses
== NULL
) {
638 log_warnx("interfaces", "unable to allocate memory");
641 TAILQ_INIT(interfaces
);
642 TAILQ_INIT(addresses
);
643 if (getifaddrs(&ifaddrs
) < 0) {
644 log_warnx("interfaces", "unable to get list of interfaces");
648 for (ifaddr
= ifaddrs
;
650 ifaddr
= ifaddr
->ifa_next
) {
651 ifbsd_extract(cfg
, interfaces
, addresses
, ifaddr
);
653 /* Link interfaces together if needed */
654 TAILQ_FOREACH(iface
, interfaces
, next
) {
655 ifbsd_check_bridge(cfg
, interfaces
, iface
);
656 ifbsd_check_bond(cfg
, interfaces
, iface
);
657 ifbsd_check_vlan(cfg
, interfaces
, iface
);
658 ifbsd_check_physical(cfg
, interfaces
, iface
);
661 interfaces_helper_whitelist(cfg
, interfaces
);
662 interfaces_helper_physical(cfg
, interfaces
,
663 ð_ops
, ifbsd_phys_init
);
665 interfaces_helper_vlan(cfg
, interfaces
);
667 interfaces_helper_mgmt(cfg
, addresses
);
668 interfaces_helper_chassis(cfg
, interfaces
);
671 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
) {
672 if (!hardware
->h_flags
) continue;
673 ifbsd_macphy(cfg
, hardware
);
677 interfaces_free_devices(interfaces
);
678 interfaces_free_addresses(addresses
);
679 if (ifaddrs
) freeifaddrs(ifaddrs
);