static void rtnl_print_ifinfo(struct nlmsghdr *hdr) { struct ifinfomsg *ifi = NLMSG_DATA(hdr); struct rtattr *attr = IFLA_RTA(ifi); uint32_t attrs_len = IFLA_PAYLOAD(hdr); char flags[256]; char if_addr[64] = {}; char *af_link = "unknown"; if (hdr->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi))) return; if (ifi->ifi_family == AF_UNSPEC) af_link = "unspec"; else if (ifi->ifi_family == AF_BRIDGE) af_link = "bridge"; tprintf(" [ Link Family %d (%s%s%s)", ifi->ifi_family, colorize_start(bold), af_link, colorize_end()); tprintf(", Type %d (%s%s%s)", ifi->ifi_type, colorize_start(bold), device_type2str(ifi->ifi_type), colorize_end()); tprintf(", Index %d", ifi->ifi_index); tprintf(", Flags 0x%x (%s%s%s)", ifi->ifi_flags, colorize_start(bold), rtnl_link_flags2str(ifi->ifi_flags, flags, sizeof(flags)), colorize_end()); tprintf(", Change 0x%x (%s%s%s) ]\n", ifi->ifi_change, colorize_start(bold), rtnl_link_flags2str(ifi->ifi_change, flags, sizeof(flags)), colorize_end()); for (; RTA_OK(attr, attrs_len); attr = RTA_NEXT(attr, attrs_len)) { switch (attr->rta_type) { case IFLA_ADDRESS: rta_fmt(attr, "Address %s", device_addr2str(RTA_DATA(attr), RTA_LEN(attr), ifi->ifi_type, if_addr, sizeof(if_addr))); break; case IFLA_BROADCAST: rta_fmt(attr, "Broadcast %s", device_addr2str(RTA_DATA(attr), RTA_LEN(attr), ifi->ifi_type, if_addr, sizeof(if_addr))); break; case IFLA_IFNAME: rta_fmt(attr, "Name %s%s%s", colorize_start(bold), RTA_STR(attr), colorize_end()); break; case IFLA_MTU: rta_fmt(attr, "MTU %d", RTA_INT(attr)); break; case IFLA_LINK: rta_fmt(attr, "Link %d", RTA_INT(attr)); break; case IFLA_QDISC: rta_fmt(attr, "QDisc %s", RTA_STR(attr)); break; case IFLA_OPERSTATE: { uint8_t st = RTA_UINT8(attr); char states[256]; rta_fmt(attr, "Operation state 0x%x (%s%s%s)", st, colorize_start(bold), rtnl_link_operstate2str(st, states, sizeof(states)), colorize_end()); } break; case IFLA_LINKMODE: { uint8_t mode = RTA_UINT8(attr); char str[32]; rta_fmt(attr, "Mode 0x%x (%s%s%s)", mode, colorize_start(bold), rtnl_link_mode2str(mode, str, sizeof(str)), colorize_end()); } break; case IFLA_GROUP: rta_fmt(attr, "Group %d", RTA_INT(attr)); break; case IFLA_TXQLEN: rta_fmt(attr, "Tx queue len %d", RTA_INT(attr)); break; case IFLA_NET_NS_PID: rta_fmt(attr, "Network namespace pid %d", RTA_INT(attr)); break; case IFLA_NET_NS_FD: rta_fmt(attr, "Network namespace fd %d", RTA_INT(attr)); break; default: rta_fmt(attr, "0x%x", attr->rta_type); break; } } }
static void handle_class(struct nl_object *obj, void *arg) { struct rtnl_tc *tc = (struct rtnl_tc *) obj; struct element *e; struct rdata *rdata = arg; struct rdata ndata = { .level = rdata->level + 1, }; if (!(e = handle_tc_obj(tc, "class", rdata))) return; ndata.parent = e; if (!strcmp(rtnl_tc_get_kind(tc), "htb")) element_set_txmax(e, rtnl_htb_get_rate((struct rtnl_class *) tc)); find_classes(rtnl_tc_get_handle(tc), &ndata); find_qdiscs(rtnl_tc_get_handle(tc), &ndata); } static void find_qdiscs(uint32_t parent, struct rdata *rdata) { struct rtnl_qdisc *filter; if (!(filter = rtnl_qdisc_alloc())) return; rtnl_tc_set_parent((struct rtnl_tc *) filter, parent); nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(filter), handle_qdisc, rdata); rtnl_qdisc_put(filter); } static void find_cls(int ifindex, uint32_t parent, struct rdata *rdata) { struct nl_cache *cls_cache; if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0) return; nl_cache_foreach(cls_cache, handle_cls, rdata); nl_cache_free(cls_cache); } static void find_classes(uint32_t parent, struct rdata *rdata) { struct rtnl_class *filter; if (!(filter = rtnl_class_alloc())) return; rtnl_tc_set_parent((struct rtnl_tc *) filter, parent); nl_cache_foreach_filter(class_cache, OBJ_CAST(filter), handle_class, rdata); rtnl_class_put(filter); } static void handle_qdisc(struct nl_object *obj, void *arg) { struct rtnl_tc *tc = (struct rtnl_tc *) obj; struct element *e; struct rdata *rdata = arg; struct rdata ndata = { .level = rdata->level + 1, }; if (!(e = handle_tc_obj(tc, "qdisc", rdata))) return; ndata.parent = e; find_cls(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc), &ndata); if (rtnl_tc_get_parent(tc) == TC_H_ROOT) { find_cls(rtnl_tc_get_ifindex(tc), TC_H_ROOT, &ndata); find_classes(TC_H_ROOT, &ndata); } find_classes(rtnl_tc_get_handle(tc), &ndata); } static void handle_tc(struct element *e, struct rtnl_link *link) { struct rtnl_qdisc *qdisc; int ifindex = rtnl_link_get_ifindex(link); struct rdata rdata = { .level = 1, .parent = e, }; if (rtnl_class_alloc_cache(sock, ifindex, &class_cache) < 0) return; qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_ROOT); if (qdisc) { handle_qdisc(OBJ_CAST(qdisc), &rdata); rtnl_qdisc_put(qdisc); } qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, 0); if (qdisc) { handle_qdisc(OBJ_CAST(qdisc), &rdata); rtnl_qdisc_put(qdisc); } qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_INGRESS); if (qdisc) { handle_qdisc(OBJ_CAST(qdisc), &rdata); rtnl_qdisc_put(qdisc); } nl_cache_free(class_cache); } static void update_link_infos(struct element *e, struct rtnl_link *link) { char buf[64]; snprintf(buf, sizeof(buf), "%u", rtnl_link_get_mtu(link)); element_update_info(e, "MTU", buf); rtnl_link_flags2str(rtnl_link_get_flags(link), buf, sizeof(buf)); element_update_info(e, "Flags", buf); rtnl_link_operstate2str(rtnl_link_get_operstate(link), buf, sizeof(buf)); element_update_info(e, "Operstate", buf); snprintf(buf, sizeof(buf), "%u", rtnl_link_get_ifindex(link)); element_update_info(e, "IfIndex", buf); nl_addr2str(rtnl_link_get_addr(link), buf, sizeof(buf)); element_update_info(e, "Address", buf); nl_addr2str(rtnl_link_get_broadcast(link), buf, sizeof(buf)); element_update_info(e, "Broadcast", buf); rtnl_link_mode2str(rtnl_link_get_linkmode(link), buf, sizeof(buf)); element_update_info(e, "Mode", buf); snprintf(buf, sizeof(buf), "%u", rtnl_link_get_txqlen(link)); element_update_info(e, "TXQlen", buf); nl_af2str(rtnl_link_get_family(link), buf, sizeof(buf)); element_update_info(e, "Family", buf); element_update_info(e, "Alias", rtnl_link_get_ifalias(link) ? : ""); element_update_info(e, "Qdisc", rtnl_link_get_qdisc(link) ? : ""); if (rtnl_link_get_link(link)) { snprintf(buf, sizeof(buf), "%u", rtnl_link_get_link(link)); element_update_info(e, "SlaveOfIndex", buf); } } static void do_link(struct nl_object *obj, void *arg) { struct rtnl_link *link = (struct rtnl_link *) obj; struct element *e, *e_parent = NULL; int i, master_ifindex; if (!cfg_show_all && !(rtnl_link_get_flags(link) & IFF_UP)) { /* FIXME: delete element */ return; } /* Check if the interface is a slave of another interface */ if ((master_ifindex = rtnl_link_get_link(link))) { char parent[IFNAMSIZ+1]; rtnl_link_i2name(link_cache, master_ifindex, parent, sizeof(parent)); e_parent = element_lookup(grp, parent, master_ifindex, NULL, 0); } if (!(e = element_lookup(grp, rtnl_link_get_name(link), rtnl_link_get_ifindex(link), e_parent, ELEMENT_CREAT))) return; if (e->e_flags & ELEMENT_FLAG_CREATED) { if (e->e_parent) e->e_level = e->e_parent->e_level + 1; if (element_set_key_attr(e, "bytes", "packets") || element_set_usage_attr(e, "bytes")) BUG(); /* FIXME: Update link infos every 1s or so */ update_link_infos(e, link); e->e_flags &= ~ELEMENT_FLAG_CREATED; } for (i = 0; i < ARRAY_SIZE(link_attrs); i++) { struct attr_map *m = &link_attrs[i]; uint64_t c_rx = 0, c_tx = 0; int flags = 0; if (m->rxid >= 0) { c_rx = rtnl_link_get_stat(link, m->rxid); flags |= UPDATE_FLAG_RX; } if (m->txid >= 0) { c_tx = rtnl_link_get_stat(link, m->txid); flags |= UPDATE_FLAG_TX; } attr_update(e, m->attrid, c_rx, c_tx, flags); } if (!c_notc) handle_tc(e, link); element_notify_update(e, NULL); element_lifesign(e, 1); } static void netlink_read(void) { int err; if ((err = nl_cache_resync(sock, link_cache, NULL, NULL)) < 0) { fprintf(stderr, "Unable to resync link cache: %s\n", nl_geterror(err)); goto disable; } if ((err = nl_cache_resync(sock, qdisc_cache, NULL, NULL)) < 0) { fprintf(stderr, "Unable to resync qdisc cache: %s\n", nl_geterror(err)); goto disable; } nl_cache_foreach(link_cache, do_link, NULL); return; disable: netlink_ops.m_flags &= ~BMON_MODULE_ENABLED; } static void netlink_shutdown(void) { nl_cache_free(link_cache); nl_cache_free(qdisc_cache); nl_socket_free(sock); }