2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #define INCLUDE_LINUX_IF_H
23 #include <sys/ioctl.h>
24 #include <sys/types.h>
27 #include <arpa/inet.h>
29 #include <net/if_arp.h>
30 #include <linux/if_vlan.h>
31 #include <linux/if_bonding.h>
32 #include <linux/if_bridge.h>
33 #include <linux/wireless.h>
34 #include <linux/sockios.h>
36 #define SYSFS_PATH_MAX 256
37 #define MAX_PORTS 1024
38 #define MAX_BRIDGES 1024
41 extern unsigned int if_nametoindex (__const
char *__ifname
) __THROW
;
42 extern char *if_indextoname (unsigned int __ifindex
, char *__ifname
) __THROW
;
45 old_iface_is_bridge(struct lldpd
*cfg
, const char *name
)
47 int ifindices
[MAX_BRIDGES
];
48 char ifname
[IFNAMSIZ
];
50 unsigned long args
[3] = { BRCTL_GET_BRIDGES
,
51 (unsigned long)ifindices
, MAX_BRIDGES
};
52 if ((num
= ioctl(cfg
->g_sock
, SIOCGIFBR
, args
)) < 0) {
54 LLOG_INFO("unable to get available bridges");
57 for (i
= 0; i
< num
; i
++) {
58 if (if_indextoname(ifindices
[i
], ifname
) == NULL
)
59 LLOG_INFO("unable to get name of interface %d",
61 else if (strncmp(name
, ifname
, IFNAMSIZ
) == 0)
68 iface_is_bridge(struct lldpd
*cfg
, const char *name
)
70 char path
[SYSFS_PATH_MAX
];
73 if ((snprintf(path
, SYSFS_PATH_MAX
,
74 SYSFS_CLASS_NET
"%s/" SYSFS_BRIDGE_FDB
, name
)) >= SYSFS_PATH_MAX
)
75 LLOG_WARNX("path truncated");
76 if ((f
= priv_open(path
)) < 0) {
77 return old_iface_is_bridge(cfg
, name
);
84 old_iface_is_bridged_to(struct lldpd
*cfg
, const char *slave
, const char *master
)
86 int j
, index
= if_nametoindex(slave
);
87 int ifptindices
[MAX_PORTS
];
88 unsigned long args2
[4] = { BRCTL_GET_PORT_LIST
,
89 (unsigned long)ifptindices
, MAX_PORTS
, 0 };
92 strncpy(ifr
.ifr_name
, master
, IFNAMSIZ
);
93 memset(ifptindices
, 0, sizeof(ifptindices
));
94 ifr
.ifr_data
= (char *)&args2
;
96 if (ioctl(cfg
->g_sock
, SIOCDEVPRIVATE
, &ifr
) < 0) {
97 LLOG_WARN("unable to get bridge members for %s",
102 for (j
= 0; j
< MAX_PORTS
; j
++) {
103 if (ifptindices
[j
] == index
)
111 iface_is_bridged_to(struct lldpd
*cfg
, const char *slave
, const char *master
)
113 char path
[SYSFS_PATH_MAX
];
116 /* Master should be a bridge, first */
117 if (!iface_is_bridge(cfg
, master
)) return 0;
119 if (snprintf(path
, SYSFS_PATH_MAX
,
120 SYSFS_CLASS_NET
"%s/" SYSFS_BRIDGE_PORT_SUBDIR
"/%s/port_no",
121 master
, slave
) >= SYSFS_PATH_MAX
)
122 LLOG_WARNX("path truncated");
123 if ((f
= priv_open(path
)) < 0) {
124 return old_iface_is_bridged_to(cfg
, slave
, master
);
131 iface_is_vlan(struct lldpd
*cfg
, const char *name
)
133 struct vlan_ioctl_args ifv
;
134 memset(&ifv
, 0, sizeof(ifv
));
135 ifv
.cmd
= GET_VLAN_REALDEV_NAME_CMD
;
136 if ((strlcpy(ifv
.device1
, name
, sizeof(ifv
.device1
))) >=
138 LLOG_WARNX("device name truncated");
139 if (ioctl(cfg
->g_sock
, SIOCGIFVLAN
, &ifv
) >= 0)
145 iface_is_wireless(struct lldpd
*cfg
, const char *name
)
148 strlcpy(iwr
.ifr_name
, name
, IFNAMSIZ
);
149 if (ioctl(cfg
->g_sock
, SIOCGIWNAME
, &iwr
) >= 0)
155 iface_is_bond(struct lldpd
*cfg
, const char *name
)
159 memset(&ifr
, 0, sizeof(ifr
));
160 memset(&ifb
, 0, sizeof(ifb
));
161 strlcpy(ifr
.ifr_name
, name
, sizeof(ifr
.ifr_name
));
163 if (ioctl(cfg
->g_sock
, SIOCBONDINFOQUERY
, &ifr
) >= 0)
169 iface_is_bond_slave(struct lldpd
*cfg
, const char *slave
, const char *master
,
175 memset(&ifr
, 0, sizeof(ifr
));
176 memset(&ifb
, 0, sizeof(ifb
));
177 strlcpy(ifr
.ifr_name
, master
, sizeof(ifr
.ifr_name
));
179 if (ioctl(cfg
->g_sock
, SIOCBONDINFOQUERY
, &ifr
) >= 0) {
180 while (ifb
.num_slaves
--) {
181 memset(&ifr
, 0, sizeof(ifr
));
182 memset(&ifs
, 0, sizeof(ifs
));
183 strlcpy(ifr
.ifr_name
, master
, sizeof(ifr
.ifr_name
));
185 ifs
.slave_id
= ifb
.num_slaves
;
186 if ((ioctl(cfg
->g_sock
, SIOCBONDSLAVEINFOQUERY
, &ifr
) >= 0) &&
187 (strncmp(ifs
.slave_name
, slave
, sizeof(ifs
.slave_name
)) == 0)) {
198 iface_is_enslaved(struct lldpd
*cfg
, const char *name
)
200 struct ifaddrs
*ifap
, *ifa
;
203 if (getifaddrs(&ifap
) != 0) {
204 LLOG_WARN("unable to get interface list");
207 for (ifa
= ifap
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
208 if (iface_is_bond_slave(cfg
, name
, ifa
->ifa_name
, NULL
)) {
209 master
= if_nametoindex(ifa
->ifa_name
);
219 iface_is_slave_active(struct lldpd
*cfg
, int master
, const char *slave
)
221 char mastername
[IFNAMSIZ
];
223 if (if_indextoname(master
, mastername
) == NULL
) {
224 LLOG_WARNX("unable to get master name for %s",
226 return 0; /* Safest choice */
228 if (!iface_is_bond_slave(cfg
, slave
, mastername
, &active
)) {
229 LLOG_WARNX("unable to get slave status for %s",
231 return 0; /* Safest choice */
233 return (active
== BOND_STATE_ACTIVE
);
237 iface_get_permanent_mac(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
239 int master
, f
, state
= 0;
241 const char *slaveif
= "Slave Interface: ";
242 const char *hwaddr
= "Permanent HW addr: ";
243 u_int8_t mac
[ETHER_ADDR_LEN
];
245 char path
[SYSFS_PATH_MAX
];
247 if ((master
= iface_is_enslaved(cfg
, hardware
->h_ifname
)) == -1)
249 /* We have a bond, we need to query it to get real MAC addresses */
250 if ((if_indextoname(master
, bond
)) == NULL
) {
251 LLOG_WARNX("unable to get bond name");
255 if (snprintf(path
, SYSFS_PATH_MAX
, "/proc/net/bonding/%s",
256 bond
) >= SYSFS_PATH_MAX
) {
257 LLOG_WARNX("path truncated");
260 if ((f
= priv_open(path
)) < 0) {
261 if (snprintf(path
, SYSFS_PATH_MAX
, "/proc/self/net/bonding/%s",
262 bond
) >= SYSFS_PATH_MAX
) {
263 LLOG_WARNX("path truncated");
269 LLOG_WARNX("unable to find %s in /proc/net/bonding or /proc/self/net/bonding",
273 if ((netbond
= fdopen(f
, "r")) == NULL
) {
274 LLOG_WARN("unable to read stream from %s", path
);
279 We parse the file to search "Slave Interface: ". If found, go to
282 We parse the file to search "Permanent HW addr: ". If found, we get
285 while (fgets(line
, sizeof(line
), netbond
)) {
288 if (strncmp(line
, slaveif
, strlen(slaveif
)) == 0) {
289 if (line
[strlen(line
)-1] == '\n')
290 line
[strlen(line
)-1] = '\0';
291 if (strncmp(hardware
->h_ifname
,
292 line
+ strlen(slaveif
),
293 sizeof(hardware
->h_ifname
)) == 0)
298 if (strncmp(line
, hwaddr
, strlen(hwaddr
)) == 0) {
299 if (line
[strlen(line
)-1] == '\n')
300 line
[strlen(line
)-1] = '\0';
301 if (sscanf(line
+ strlen(hwaddr
),
302 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
303 &mac
[0], &mac
[1], &mac
[2],
304 &mac
[3], &mac
[4], &mac
[5]) !=
306 LLOG_WARN("unable to parse %s",
307 line
+ strlen(hwaddr
));
311 memcpy(hardware
->h_lladdr
, mac
,
319 LLOG_WARNX("unable to find real mac address for %s",
325 #ifdef ENABLE_LLDPMED
326 /* Fill in inventory stuff:
327 - hardware version: /sys/class/dmi/id/product_version
328 - firmware version: /sys/class/dmi/id/bios_version
329 - software version: `uname -r`
330 - serial number: /sys/class/dmi/id/product_serial
331 - manufacturer: /sys/class/dmi/id/sys_vendor
332 - model: /sys/class/dmi/id/product_name
333 - asset: /sys/class/dmi/id/chassis_asset_tag
342 if ((dmi
= priv_open(file
)) < 0) {
343 LLOG_DEBUG("cannot get DMI file %s", file
);
346 memset(buffer
, 0, sizeof(buffer
));
347 if ((s
= read(dmi
, buffer
, sizeof(buffer
))) == -1) {
348 LLOG_DEBUG("cannot read DMI file %s", file
);
353 buffer
[sizeof(buffer
) - 1] = '\0';
354 if ((s
> 0) && (buffer
[s
-1] == '\n'))
357 return strdup(buffer
);
364 return dmi_get(SYSFS_CLASS_DMI
"product_version");
370 return dmi_get(SYSFS_CLASS_DMI
"bios_version");
376 return dmi_get(SYSFS_CLASS_DMI
"product_serial");
382 return dmi_get(SYSFS_CLASS_DMI
"sys_vendor");
388 return dmi_get(SYSFS_CLASS_DMI
"product_name");
394 return dmi_get(SYSFS_CLASS_DMI
"chassis_asset_tag");