]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/features.c
Move chassis update to a function.
[thirdparty/lldpd.git] / src / features.c
1 /*
2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
3 *
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.
7 *
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.
15 */
16
17 #define INCLUDE_LINUX_IF_H
18 #include "lldpd.h"
19
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <errno.h>
23 #include <sys/ioctl.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <arpa/inet.h>
28 #include <ifaddrs.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>
35
36 #define SYSFS_PATH_MAX 256
37 #define MAX_PORTS 1024
38 #define MAX_BRIDGES 1024
39
40 /* net/if.h */
41 extern unsigned int if_nametoindex (__const char *__ifname) __THROW;
42 extern char *if_indextoname (unsigned int __ifindex, char *__ifname) __THROW;
43
44 static int
45 old_iface_is_bridge(struct lldpd *cfg, const char *name)
46 {
47 int ifindices[MAX_BRIDGES];
48 char ifname[IFNAMSIZ];
49 int num, i;
50 unsigned long args[3] = { BRCTL_GET_BRIDGES,
51 (unsigned long)ifindices, MAX_BRIDGES };
52 if ((num = ioctl(cfg->g_sock, SIOCGIFBR, args)) < 0) {
53 if (errno != ENOPKG)
54 LLOG_INFO("unable to get available bridges");
55 return 0;
56 }
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",
60 ifindices[i]);
61 else if (strncmp(name, ifname, IFNAMSIZ) == 0)
62 return 1;
63 }
64 return 0;
65 }
66
67 int
68 iface_is_bridge(struct lldpd *cfg, const char *name)
69 {
70 char path[SYSFS_PATH_MAX];
71 int f;
72
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);
78 }
79 close(f);
80 return 1;
81 }
82
83 static int
84 old_iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master)
85 {
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 };
90 struct ifreq ifr;
91
92 strncpy(ifr.ifr_name, master, IFNAMSIZ);
93 memset(ifptindices, 0, sizeof(ifptindices));
94 ifr.ifr_data = (char *)&args2;
95
96 if (ioctl(cfg->g_sock, SIOCDEVPRIVATE, &ifr) < 0) {
97 LLOG_WARN("unable to get bridge members for %s",
98 ifr.ifr_name);
99 return 0;
100 }
101
102 for (j = 0; j < MAX_PORTS; j++) {
103 if (ifptindices[j] == index)
104 return 1;
105 }
106
107 return 0;
108 }
109
110 int
111 iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master)
112 {
113 char path[SYSFS_PATH_MAX];
114 int f;
115
116 /* Master should be a bridge, first */
117 if (!iface_is_bridge(cfg, master)) return 0;
118
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);
125 }
126 close(f);
127 return 1;
128 }
129
130 int
131 iface_is_vlan(struct lldpd *cfg, const char *name)
132 {
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))) >=
137 sizeof(ifv.device1))
138 LLOG_WARNX("device name truncated");
139 if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0)
140 return 1;
141 return 0;
142 }
143
144 int
145 iface_is_wireless(struct lldpd *cfg, const char *name)
146 {
147 struct iwreq iwr;
148 strlcpy(iwr.ifr_name, name, IFNAMSIZ);
149 if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0)
150 return 1;
151 return 0;
152 }
153
154 int
155 iface_is_bond(struct lldpd *cfg, const char *name)
156 {
157 struct ifreq ifr;
158 struct ifbond ifb;
159 memset(&ifr, 0, sizeof(ifr));
160 memset(&ifb, 0, sizeof(ifb));
161 strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
162 ifr.ifr_data = &ifb;
163 if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0)
164 return 1;
165 return 0;
166 }
167
168 int
169 iface_is_bond_slave(struct lldpd *cfg, const char *slave, const char *master,
170 int *active)
171 {
172 struct ifreq ifr;
173 struct ifbond ifb;
174 struct ifslave ifs;
175 memset(&ifr, 0, sizeof(ifr));
176 memset(&ifb, 0, sizeof(ifb));
177 strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name));
178 ifr.ifr_data = &ifb;
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));
184 ifr.ifr_data = &ifs;
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)) {
188 if (active)
189 *active = ifs.state;
190 return 1;
191 }
192 }
193 }
194 return 0;
195 }
196
197 int
198 iface_is_enslaved(struct lldpd *cfg, const char *name)
199 {
200 struct ifaddrs *ifap, *ifa;
201 int master;
202
203 if (getifaddrs(&ifap) != 0) {
204 LLOG_WARN("unable to get interface list");
205 return -1;
206 }
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);
210 freeifaddrs(ifap);
211 return master;
212 }
213 }
214 freeifaddrs(ifap);
215 return -1;
216 }
217
218 int
219 iface_is_slave_active(struct lldpd *cfg, int master, const char *slave)
220 {
221 char mastername[IFNAMSIZ];
222 int active;
223 if (if_indextoname(master, mastername) == NULL) {
224 LLOG_WARNX("unable to get master name for %s",
225 slave);
226 return 0; /* Safest choice */
227 }
228 if (!iface_is_bond_slave(cfg, slave, mastername, &active)) {
229 LLOG_WARNX("unable to get slave status for %s",
230 slave);
231 return 0; /* Safest choice */
232 }
233 return (active == BOND_STATE_ACTIVE);
234 }
235
236 void
237 iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware)
238 {
239 int master, f, state = 0;
240 FILE *netbond;
241 const char *slaveif = "Slave Interface: ";
242 const char *hwaddr = "Permanent HW addr: ";
243 u_int8_t mac[ETHER_ADDR_LEN];
244 char bond[IFNAMSIZ];
245 char path[SYSFS_PATH_MAX];
246 char line[100];
247 if ((master = iface_is_enslaved(cfg, hardware->h_ifname)) == -1)
248 return;
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");
252 return;
253 }
254
255 if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s",
256 bond) >= SYSFS_PATH_MAX) {
257 LLOG_WARNX("path truncated");
258 return;
259 }
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");
264 return;
265 }
266 f = priv_open(path);
267 }
268 if (f < 0) {
269 LLOG_WARNX("unable to find %s in /proc/net/bonding or /proc/self/net/bonding",
270 bond);
271 return;
272 }
273 if ((netbond = fdopen(f, "r")) == NULL) {
274 LLOG_WARN("unable to read stream from %s", path);
275 close(f);
276 return;
277 }
278 /* State 0:
279 We parse the file to search "Slave Interface: ". If found, go to
280 state 1.
281 State 1:
282 We parse the file to search "Permanent HW addr: ". If found, we get
283 the mac.
284 */
285 while (fgets(line, sizeof(line), netbond)) {
286 switch (state) {
287 case 0:
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)
294 state++;
295 }
296 break;
297 case 1:
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]) !=
305 ETHER_ADDR_LEN) {
306 LLOG_WARN("unable to parse %s",
307 line + strlen(hwaddr));
308 fclose(netbond);
309 return;
310 }
311 memcpy(hardware->h_lladdr, mac,
312 ETHER_ADDR_LEN);
313 fclose(netbond);
314 return;
315 }
316 break;
317 }
318 }
319 LLOG_WARNX("unable to find real mac address for %s",
320 bond);
321 fclose(netbond);
322 }
323
324
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
334 */
335
336 char*
337 dmi_get(char *file)
338 {
339 int dmi, s;
340 char buffer[100];
341
342 if ((dmi = priv_open(file)) < 0) {
343 LLOG_DEBUG("cannot get DMI file %s", file);
344 return NULL;
345 }
346 memset(buffer, 0, sizeof(buffer));
347 if ((s = read(dmi, buffer, sizeof(buffer))) == -1) {
348 LLOG_DEBUG("cannot read DMI file %s", file);
349 close(dmi);
350 return NULL;
351 }
352 close(dmi);
353 buffer[sizeof(buffer) - 1] = '\0';
354 if ((s > 0) && (buffer[s-1] == '\n'))
355 buffer[s-1] = '\0';
356 if (strlen(buffer))
357 return strdup(buffer);
358 return NULL;
359 }
360
361 char*
362 dmi_hw()
363 {
364 return dmi_get(SYSFS_CLASS_DMI "product_version");
365 }
366
367 char*
368 dmi_fw()
369 {
370 return dmi_get(SYSFS_CLASS_DMI "bios_version");
371 }
372
373 char*
374 dmi_sn()
375 {
376 return dmi_get(SYSFS_CLASS_DMI "product_serial");
377 }
378
379 char*
380 dmi_manuf()
381 {
382 return dmi_get(SYSFS_CLASS_DMI "sys_vendor");
383 }
384
385 char*
386 dmi_model()
387 {
388 return dmi_get(SYSFS_CLASS_DMI "product_name");
389 }
390
391 char*
392 dmi_asset()
393 {
394 return dmi_get(SYSFS_CLASS_DMI "chassis_asset_tag");
395 }
396 #endif