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/sysctl.h>
23 #include <sys/ioctl.h>
25 #include <net/if_types.h>
26 #include <net/if_mib.h>
27 #include <net/if_media.h>
28 #include <net/if_vlan_var.h>
29 #include <net/if_bridgevar.h>
30 #include <net/if_lagg.h>
31 #include <net/if_dl.h>
34 #define IFDESCRSIZE 64
38 ifbsd_check_driver(struct lldpd
*cfg
,
39 struct ifaddrs
*ifaddr
,
40 struct interfaces_device
*iface
)
43 char dname
[IFNAMSIZ
+1] = {};
44 size_t len
= IFNAMSIZ
;
48 name
[2] = NETLINK_GENERIC
;
49 name
[3] = IFMIB_IFDATA
;
50 name
[4] = iface
->index
;
51 name
[5] = IFDATA_DRIVERNAME
;
53 if (sysctl(name
, 6, dname
, &len
, 0, 0) == -1 || len
== 0) {
54 log_info("interfaces", "unable to get driver name for %s",
58 iface
->driver
= strdup(dname
);
63 ifbsd_check_wireless(struct lldpd
*cfg
,
64 struct ifaddrs
*ifaddr
,
65 struct interfaces_device
*iface
)
67 struct ifmediareq ifmr
= {};
68 strlcpy(ifmr
.ifm_name
, iface
->name
, sizeof(ifmr
.ifm_name
));
69 if (ioctl(cfg
->g_sock
, SIOCGIFMEDIA
, (caddr_t
)&ifmr
) < 0 ||
70 IFM_TYPE(ifmr
.ifm_current
) != IFM_IEEE80211
)
71 return 0; /* Not wireless either */
72 iface
->type
|= IFACE_WIRELESS_T
| IFACE_PHYSICAL_T
;
77 ifbsd_check_bridge(struct lldpd
*cfg
,
78 struct interfaces_device_list
*interfaces
,
79 struct interfaces_device
*master
)
81 struct ifbreq req
[64];
82 struct ifbifconf bifc
= {
83 .ifbic_len
= sizeof(req
),
88 .ifd_len
= sizeof(bifc
),
92 strlcpy(ifd
.ifd_name
, master
->name
, sizeof(ifd
.ifd_name
));
93 if (ioctl(cfg
->g_sock
, SIOCGDRVSPEC
, (caddr_t
)&ifd
) < 0) {
94 log_debug("interfaces",
95 "%s is not a bridge", master
->name
);
98 if (bifc
.ifbic_len
>= sizeof(req
)) {
99 log_warnx("interfaces",
100 "%s is a bridge too big. Please, report the problem",
104 for (int i
= 0; i
< bifc
.ifbic_len
/ sizeof(*req
); i
++) {
105 struct interfaces_device
*slave
=
106 interfaces_nametointerface(interfaces
,
107 req
[i
].ifbr_ifsname
);
109 log_warnx("interfaces",
110 "%s should be bridged to %s but we don't know %s",
111 req
[i
].ifbr_ifsname
, master
->name
, req
[i
].ifbr_ifsname
);
114 log_debug("interfaces",
115 "%s is bridged to %s",
116 slave
->name
, master
->name
);
117 slave
->upper
= master
;
119 master
->type
|= IFACE_BRIDGE_T
;
123 ifbsd_check_bond(struct lldpd
*cfg
,
124 struct interfaces_device_list
*interfaces
,
125 struct interfaces_device
*master
)
127 struct lagg_reqport rpbuf
[LAGG_MAX_PORTS
];
128 struct lagg_reqall ra
= {
129 .ra_size
= sizeof(rpbuf
),
132 strlcpy(ra
.ra_ifname
, master
->name
, IFNAMSIZ
);
133 if (ioctl(cfg
->g_sock
, SIOCGLAGG
, (caddr_t
)&ra
) < 0) {
134 log_debug("interfaces",
135 "%s is not a bond", master
->name
);
139 for (int i
= 0; i
< ra
.ra_ports
; i
++) {
140 struct interfaces_device
*slave
;
141 slave
= interfaces_nametointerface(interfaces
,
142 rpbuf
[i
].rp_portname
);
144 log_warnx("interfaces",
145 "%s should be enslaved to %s but we don't know %s",
146 rpbuf
[i
].rp_portname
, master
->name
,
147 rpbuf
[i
].rp_portname
);
150 log_debug("interfaces",
151 "%s is enslaved to bond %s",
152 slave
->name
, master
->name
);
153 slave
->upper
= master
;
155 master
->type
|= IFACE_BOND_T
;
159 ifbsd_check_vlan(struct lldpd
*cfg
,
160 struct interfaces_device_list
*interfaces
,
161 struct interfaces_device
*vlan
)
163 struct interfaces_device
*lower
;
164 struct vlanreq vreq
= {};
166 .ifr_data
= (caddr_t
)&vreq
168 strlcpy(ifr
.ifr_name
, vlan
->name
, sizeof(ifr
.ifr_name
));
169 if (ioctl(cfg
->g_sock
, SIOCGETVLAN
, (caddr_t
)&ifr
) < 0) {
170 log_debug("interfaces",
171 "%s is not a VLAN", vlan
->name
);
174 if (strlen(vreq
.vlr_parent
) == 0) {
175 log_debug("interfaces",
176 "%s is a VLAN but has no lower interface",
179 vlan
->type
|= IFACE_VLAN_T
;
182 lower
= interfaces_nametointerface(interfaces
,
185 log_warnx("interfaces",
186 "%s should be a VLAN of %s but %s does not exist",
187 vlan
->name
, vreq
.vlr_parent
, vreq
.vlr_parent
);
190 log_debug("interfaces",
191 "%s is VLAN %d of %s",
192 vlan
->name
, vreq
.vlr_tag
, lower
->name
);
194 vlan
->vlanid
= vreq
.vlr_tag
;
195 vlan
->type
|= IFACE_VLAN_T
;
199 ifbsd_check_physical(struct lldpd
*cfg
,
200 struct interfaces_device_list
*interfaces
,
201 struct interfaces_device
*iface
)
203 if (iface
->type
& (IFACE_VLAN_T
|
204 IFACE_BOND_T
|IFACE_BRIDGE_T
|IFACE_PHYSICAL_T
))
207 if (!(iface
->flags
& (IFF_MULTICAST
|IFF_BROADCAST
))) {
208 log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
212 log_debug("interfaces",
213 "%s is a physical interface",
215 iface
->type
|= IFACE_PHYSICAL_T
;
218 static struct interfaces_device
*
219 ifbsd_extract_device(struct lldpd
*cfg
,
220 struct ifaddrs
*ifaddr
)
222 struct interfaces_device
*iface
= NULL
;
223 struct sockaddr_dl
*saddrdl
= (struct sockaddr_dl
*)ifaddr
->ifa_addr
;
224 if ((saddrdl
->sdl_type
!= IFT_BRIDGE
) &&
225 (saddrdl
->sdl_type
!= IFT_L2VLAN
) &&
226 (saddrdl
->sdl_type
!= IFT_ETHER
)) {
227 log_debug("interfaces", "skip %s: not an ethernet device (%d)",
228 ifaddr
->ifa_name
, saddrdl
->sdl_type
);
231 if ((iface
= calloc(1, sizeof(struct interfaces_device
))) == NULL
) {
232 log_warn("interfaces", "unable to allocate memory for %s",
237 iface
->index
= saddrdl
->sdl_index
;
238 iface
->name
= strdup(ifaddr
->ifa_name
);
239 iface
->flags
= ifaddr
->ifa_flags
;
242 iface
->address
= malloc(ETHER_ADDR_LEN
);
244 memcpy(iface
->address
, LLADDR(saddrdl
), ETHER_ADDR_LEN
);
246 /* Grab description */
247 iface
->alias
= malloc(IFDESCRSIZE
);
250 .ifr_buffer
= { .buffer
= iface
->alias
,
251 .length
= IFDESCRSIZE
}
253 strlcpy(ifr
.ifr_name
, ifaddr
->ifa_name
, sizeof(ifr
.ifr_name
));
254 if (ioctl(cfg
->g_sock
, SIOCGIFDESCR
, (caddr_t
)&ifr
) < 0) {
260 if (ifbsd_check_driver(cfg
, ifaddr
, iface
) == -1 ||
261 ifbsd_check_wireless(cfg
, ifaddr
, iface
) == -1) {
262 interfaces_free_device(iface
);
270 ifbsd_extract(struct lldpd
*cfg
,
271 struct interfaces_device_list
*interfaces
,
272 struct interfaces_address_list
*addresses
,
273 struct ifaddrs
*ifaddr
)
275 struct interfaces_address
*address
= NULL
;
276 struct interfaces_device
*device
= NULL
;
277 if (!ifaddr
->ifa_name
) return;
278 if (!ifaddr
->ifa_addr
) return;
279 if (!(ifaddr
->ifa_flags
& IFF_UP
)) {
280 log_debug("interfaces",
281 "skip %s: down", ifaddr
->ifa_name
);
284 switch (ifaddr
->ifa_addr
->sa_family
) {
286 log_debug("interfaces",
287 "grabbing information on interface %s",
289 device
= ifbsd_extract_device(cfg
, ifaddr
);
291 TAILQ_INSERT_TAIL(interfaces
, device
, next
);
295 log_debug("interfaces",
296 "got an IP address on %s",
298 address
= malloc(sizeof(struct interfaces_address
));
299 if (address
== NULL
) {
300 log_warn("interfaces",
301 "not enough memory for a new IP address on %s",
305 address
->flags
= ifaddr
->ifa_flags
;
306 address
->index
= if_nametoindex(ifaddr
->ifa_name
);
307 memcpy(&address
->address
,
309 (ifaddr
->ifa_addr
->sa_family
== AF_INET
)?
310 sizeof(struct sockaddr_in
):
311 sizeof(struct sockaddr_in6
));
312 TAILQ_INSERT_TAIL(addresses
, address
, next
);
315 log_debug("interfaces", "unhandled family %d for interface %s",
316 ifaddr
->ifa_addr
->sa_family
,
322 ifbsd_macphy(struct lldpd
*cfg
,
323 struct lldpd_hardware
*hardware
)
326 int media_list
[32] = {};
327 struct ifmediareq ifmr
= {
328 .ifm_ulist
= media_list
,
329 .ifm_count
= sizeof(media_list
) / sizeof(int)
331 struct lldpd_port
*port
= &hardware
->h_lport
;
334 int advertised_ifmedia_to_rfc3636
[][3] = {
336 LLDP_DOT3_LINK_AUTONEG_10BASE_T
,
337 LLDP_DOT3_LINK_AUTONEG_10BASET_FD
},
339 LLDP_DOT3_LINK_AUTONEG_10BASE_T
,
340 LLDP_DOT3_LINK_AUTONEG_10BASET_FD
},
342 LLDP_DOT3_LINK_AUTONEG_100BASE_TX
,
343 LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD
},
345 LLDP_DOT3_LINK_AUTONEG_100BASE_T4
,
346 LLDP_DOT3_LINK_AUTONEG_100BASE_T4
},
348 LLDP_DOT3_LINK_AUTONEG_100BASE_T2
,
349 LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD
},
351 LLDP_DOT3_LINK_AUTONEG_1000BASE_X
,
352 LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD
},
354 LLDP_DOT3_LINK_AUTONEG_1000BASE_X
,
355 LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD
},
357 LLDP_DOT3_LINK_AUTONEG_1000BASE_X
,
358 LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD
},
360 LLDP_DOT3_LINK_AUTONEG_1000BASE_T
,
361 LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD
},
364 int current_ifmedia_to_rfc3636
[][3] = {
366 LLDP_DOT3_MAU_10BASETHD
, LLDP_DOT3_MAU_10BASETFD
},
368 LLDP_DOT3_MAU_10BASETHD
, LLDP_DOT3_MAU_10BASETFD
},
370 LLDP_DOT3_MAU_10BASE2
, LLDP_DOT3_MAU_10BASE2
},
372 LLDP_DOT3_MAU_10BASE5
, LLDP_DOT3_MAU_10BASE5
},
374 LLDP_DOT3_MAU_100BASETXHD
, LLDP_DOT3_MAU_100BASETXFD
},
376 LLDP_DOT3_MAU_100BASEFXHD
, LLDP_DOT3_MAU_100BASEFXFD
},
378 LLDP_DOT3_MAU_100BASET2HD
, LLDP_DOT3_MAU_100BASET2FD
},
380 LLDP_DOT3_MAU_1000BASESXHD
, LLDP_DOT3_MAU_1000BASESXFD
},
382 LLDP_DOT3_MAU_10BASEFLHD
, LLDP_DOT3_MAU_10BASEFLFD
},
384 LLDP_DOT3_MAU_1000BASELXHD
, LLDP_DOT3_MAU_1000BASELXFD
},
386 LLDP_DOT3_MAU_1000BASECXHD
, LLDP_DOT3_MAU_1000BASECXFD
},
388 LLDP_DOT3_MAU_1000BASETHD
, LLDP_DOT3_MAU_1000BASETFD
},
390 LLDP_DOT3_MAU_10GIGBASELR
, LLDP_DOT3_MAU_10GIGBASELR
},
392 LLDP_DOT3_MAU_10GIGBASESR
, LLDP_DOT3_MAU_10GIGBASESR
},
394 LLDP_DOT3_MAU_10GIGBASELX4
, LLDP_DOT3_MAU_10GIGBASELX4
},
398 log_debug("interfaces", "get MAC/phy for %s",
400 strlcpy(ifmr
.ifm_name
, hardware
->h_ifname
, sizeof(ifmr
.ifm_name
));
401 if (ioctl(cfg
->g_sock
, SIOCGIFMEDIA
, (caddr_t
)&ifmr
) < 0) {
402 log_warn("interfaces",
403 "unable to get media information from %s",
407 if (IFM_TYPE(ifmr
.ifm_current
) != IFM_ETHER
) {
408 log_warnx("interfaces",
409 "cannot get media information from %s: not an ethernet device",
413 if ((ifmr
.ifm_status
& IFM_ACTIVE
) == 0) {
414 log_debug("interfaces",
415 "interface %s is now down, skip",
419 if (ifmr
.ifm_count
== 0) {
420 log_warnx("interfaces", "no media information available on %s",
424 port
->p_macphy
.autoneg_support
=
425 port
->p_macphy
.autoneg_enabled
= 0;
426 for (int m
= 0; m
< ifmr
.ifm_count
; m
++) {
427 media
= IFM_SUBTYPE(ifmr
.ifm_ulist
[m
]);
428 duplex
= !!(IFM_OPTIONS(ifmr
.ifm_ulist
[m
]) &
430 if (media
== IFM_AUTO
) {
431 port
->p_macphy
.autoneg_support
= 1;
432 port
->p_macphy
.autoneg_enabled
=
433 (IFM_SUBTYPE(ifmr
.ifm_current
) == IFM_AUTO
);
437 for (int j
= 0; advertised_ifmedia_to_rfc3636
[j
][0]; j
++) {
438 if (advertised_ifmedia_to_rfc3636
[j
][0] == media
) {
439 port
->p_macphy
.autoneg_advertised
|=
440 advertised_ifmedia_to_rfc3636
[j
][1 + duplex
];
446 port
->p_macphy
.mau_type
= 0;
447 media
= IFM_SUBTYPE(ifmr
.ifm_active
);
448 duplex
= !!(IFM_OPTIONS(ifmr
.ifm_active
) & IFM_FDX
);
449 for (int j
= 0; current_ifmedia_to_rfc3636
[j
][0]; j
++) {
450 if (current_ifmedia_to_rfc3636
[j
][0] == media
) {
451 port
->p_macphy
.mau_type
=
452 current_ifmedia_to_rfc3636
[j
][1 + duplex
];
460 size_t len
; /* Total length of the buffer */
461 char data
[0]; /* Data */
465 ifbsd_phys_init(struct lldpd
*cfg
,
466 struct lldpd_hardware
*hardware
)
468 struct bpf_insn filter
[] = { LLDPD_FILTER_F
};
469 struct ifreq ifr
= {};
470 struct bpf_program fprog
= {
472 .bf_len
= sizeof(filter
)/sizeof(struct bpf_insn
)
474 struct bpf_buffer
*buffer
= NULL
;
475 int fd
= -1, enable
, required
;
477 log_debug("interfaces", "initialize ethernet device %s",
479 if ((fd
= priv_iface_init(hardware
->h_ifindex
)) == -1)
481 /* We got a file descriptor to /dev/bpfXXX */
483 /* Set buffer size */
484 required
= ETHER_MAX_LEN
;
485 if (ioctl(fd
, BIOCSBLEN
, (caddr_t
)&required
) < 0) {
486 log_warn("interfaces",
487 "unable to set receive buffer size for BPF on %s",
491 hardware
->h_data
= buffer
=
492 malloc(required
+ sizeof(struct bpf_buffer
));
493 if (buffer
== NULL
) {
494 log_warn("interfaces",
495 "unable to allocate buffer space for BPF on %s",
499 buffer
->len
= required
;
501 /* Bind the interface to BPF device */
502 strlcpy(ifr
.ifr_name
, hardware
->h_ifname
, IFNAMSIZ
);
503 if (ioctl(fd
, BIOCSETIF
, (caddr_t
)&ifr
) < 0) {
504 log_warn("interfaces", "failed to bind interface %s to BPF",
509 /* Disable buffering */
511 if (ioctl(fd
, BIOCIMMEDIATE
, (caddr_t
)&enable
) < 0) {
512 log_warn("interfaces", "unable to disable buffering for %s",
517 /* Let us write the MAC address (raw packet mode) */
519 if (ioctl(fd
, BIOCSHDRCMPLT
, (caddr_t
)&enable
) < 0) {
520 log_warn("interfaces",
521 "unable to set the `header complete` flag for %s",
526 /* We only want to receive incoming packets */
528 if (ioctl(fd
, BIOCSDIRECTION
, (caddr_t
)&enable
) < 0) {
529 log_warn("interfaces",
530 "unable to set packet direction for BPF filter on %s",
535 /* Install read filter */
536 if (ioctl(fd
, BIOCSETF
, (caddr_t
)&fprog
) < 0) {
537 log_warn("interfaces", "unable to setup BPF filter for %s",
541 /* Install write filter (optional) */
542 if (ioctl(fd
, BIOCSETWF
, (caddr_t
)&fprog
) < 0) {
543 log_info("interfaces", "unable to setup write BPF filter for %s",
547 /* Setup multicast */
548 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 0);
550 hardware
->h_sendfd
= fd
; /* Send */
552 levent_hardware_add_fd(hardware
, fd
); /* Receive */
553 log_debug("interfaces", "interface %s initialized (fd=%d)", hardware
->h_ifname
,
558 if (fd
>= 0) close(fd
);
560 hardware
->h_data
= NULL
;
564 /* Ethernet send/receive through BPF */
566 ifbsd_eth_send(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
,
567 char *buffer
, size_t size
)
569 log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)",
570 hardware
->h_ifname
, hardware
->h_sendfd
);
571 return write(hardware
->h_sendfd
,
576 ifbsd_eth_recv(struct lldpd
*cfg
,
577 struct lldpd_hardware
*hardware
,
578 int fd
, char *buffer
, size_t size
)
581 struct bpf_buffer
*bpfbuf
= hardware
->h_data
;
583 log_debug("interfaces", "receive PDU from ethernet device %s",
586 /* We assume we have only receive one packet (unbuffered mode). Dunno if
587 * this is correct. */
588 if ((n
= read(fd
, bpfbuf
->data
, bpfbuf
->len
)) == -1) {
589 log_warn("interfaces", "error while receiving frame on %s",
591 hardware
->h_rx_discarded_cnt
++;
594 bh
= (struct bpf_hdr
*)bpfbuf
->data
;
595 if (bh
->bh_caplen
< size
)
596 size
= bh
->bh_caplen
;
597 memcpy(buffer
, bpfbuf
->data
+ bh
->bh_hdrlen
, size
);
603 ifbsd_eth_close(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
605 log_debug("interfaces", "close ethernet device %s",
607 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 1);
611 static struct lldpd_ops eth_ops
= {
612 .send
= ifbsd_eth_send
,
613 .recv
= ifbsd_eth_recv
,
614 .cleanup
= ifbsd_eth_close
,
618 interfaces_update(struct lldpd
*cfg
)
620 struct lldpd_hardware
*hardware
;
621 struct interfaces_device
*iface
;
622 struct interfaces_device_list
*interfaces
= NULL
;
623 struct interfaces_address_list
*addresses
= NULL
;
624 struct ifaddrs
*ifaddrs
= NULL
, *ifaddr
;
626 interfaces
= malloc(sizeof(struct interfaces_device_list
));
627 addresses
= malloc(sizeof(struct interfaces_address_list
));
628 if (interfaces
== NULL
|| addresses
== NULL
) {
629 log_warnx("interfaces", "unable to allocate memory");
632 TAILQ_INIT(interfaces
);
633 TAILQ_INIT(addresses
);
634 if (getifaddrs(&ifaddrs
) < 0) {
635 log_warnx("interfaces", "unable to get list of interfaces");
639 for (ifaddr
= ifaddrs
;
641 ifaddr
= ifaddr
->ifa_next
) {
642 ifbsd_extract(cfg
, interfaces
, addresses
, ifaddr
);
644 /* Link interfaces together if needed */
645 TAILQ_FOREACH(iface
, interfaces
, next
) {
646 ifbsd_check_bridge(cfg
, interfaces
, iface
);
647 ifbsd_check_bond(cfg
, interfaces
, iface
);
648 ifbsd_check_vlan(cfg
, interfaces
, iface
);
649 ifbsd_check_physical(cfg
, interfaces
, iface
);
652 interfaces_helper_whitelist(cfg
, interfaces
);
653 interfaces_helper_physical(cfg
, interfaces
,
654 ð_ops
, ifbsd_phys_init
);
656 interfaces_helper_vlan(cfg
, interfaces
);
658 interfaces_helper_mgmt(cfg
, addresses
);
659 interfaces_helper_chassis(cfg
, interfaces
);
662 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
) {
663 if (!hardware
->h_flags
) continue;
664 ifbsd_macphy(cfg
, hardware
);
668 interfaces_free_devices(interfaces
);
669 interfaces_free_addresses(addresses
);
670 if (ifaddrs
) freeifaddrs(ifaddrs
);