static struct element *handle_tc_obj(struct rtnl_tc *tc, const char *prefix, struct rdata *rdata) { char buf[IFNAME_MAX], name[IFNAME_MAX]; uint32_t id = rtnl_tc_get_handle(tc); struct element *e; rtnl_tc_handle2str(id, buf, sizeof(buf)); snprintf(name, sizeof(name), "%s %s (%s)", prefix, buf, rtnl_tc_get_kind(tc)); if (!(e = element_lookup(grp, name, id, rdata ? rdata->parent : NULL, ELEMENT_CREAT))) return NULL; if (e->e_flags & ELEMENT_FLAG_CREATED) { e->e_level = rdata ? rdata->level : 0; if (element_set_key_attr(e, "tc_bytes", "tc_packets") || element_set_usage_attr(e, "tc_bytes")) BUG(); update_tc_infos(e, tc); e->e_flags &= ~ELEMENT_FLAG_CREATED; } update_tc_attrs(e, tc); element_notify_update(e, NULL); element_lifesign(e, 1); return e; }
bool TNlHtb::Valid(const TNlLink &link, uint32_t defaultClass) { int ret; struct nl_cache *qdiscCache; bool valid = true; ret = rtnl_qdisc_alloc_cache(link.GetSock(), &qdiscCache); if (ret < 0) { L_ERR() << "can't alloc qdisc cache" << std::endl; return false; } struct rtnl_qdisc *qdisc = rtnl_qdisc_get(qdiscCache, link.GetIndex(), Handle); if (qdisc) { link.Dump("found", qdisc); if (rtnl_tc_get_ifindex(TC_CAST(qdisc)) != link.GetIndex()) valid = false; else if (rtnl_tc_get_parent(TC_CAST(qdisc)) != Parent) valid = false; else if (rtnl_tc_get_handle(TC_CAST(qdisc)) != Handle) valid = false; else if (rtnl_tc_get_kind(TC_CAST(qdisc)) != std::string("htb")) valid = false; else if (rtnl_htb_get_defcls(qdisc) != TC_H_MIN(defaultClass)) valid = false; } else { valid = false; } rtnl_qdisc_put(qdisc); nl_cache_free(qdiscCache); return valid; }
TError TNlClass::Load(const TNl &nl) { struct nl_cache *cache; struct rtnl_class *tclass; int ret = rtnl_class_alloc_cache(nl.GetSock(), Index, &cache); if (ret < 0) return nl.Error(ret, "Cannot allocate class cache"); tclass = rtnl_class_get(cache, Index, Handle); if (!tclass) { nl_cache_free(cache); return TError(EError::Unknown, "Can't find tc class"); } Kind = rtnl_tc_get_kind(TC_CAST(tclass)); if (Kind == "htb") { Prio = rtnl_htb_get_prio(tclass); Rate = rtnl_htb_get_rate(tclass); Ceil = rtnl_htb_get_ceil(tclass); } if (Kind == "hfsc") { struct tc_service_curve sc; if (!rtnl_class_hfsc_get_fsc(tclass, &sc)) Rate = sc.m2; if (!rtnl_class_hfsc_get_usc(tclass, &sc)) Ceil = sc.m2; } rtnl_class_put(tclass); nl_cache_free(cache); return TError::Success(); }
Result<htb::Config> decode<htb::Config>( const Netlink<struct rtnl_qdisc>& qdisc) { if (rtnl_tc_get_kind(TC_CAST(qdisc.get())) != htb::KIND) { return None(); } return htb::Config(); }
Result<basic::Classifier> decode<basic::Classifier>( const Netlink<struct rtnl_cls>& cls) { if (rtnl_tc_get_kind(TC_CAST(cls.get())) != string("basic")) { return None(); } return basic::Classifier(rtnl_cls_get_protocol(cls.get())); }
Result<ingress::Discipline> decode<ingress::Discipline>( const Netlink<struct rtnl_qdisc>& qdisc) { if (rtnl_tc_get_kind(TC_CAST(qdisc.get())) != string("ingress") || rtnl_tc_get_parent(TC_CAST(qdisc.get())) != INGRESS_ROOT.get() || rtnl_tc_get_handle(TC_CAST(qdisc.get())) != ingress::HANDLE.get()) { return None(); } return ingress::Discipline(); }
bool TNlQdisc::Check(const TNl &nl) { struct nl_cache *qdiscCache; bool result = false; int ret; ret = rtnl_qdisc_alloc_cache(nl.GetSock(), &qdiscCache); if (ret < 0) { L_ERR() << nl.Error(ret, "cannot alloc qdisc cache") << std::endl; return false; } struct rtnl_qdisc *qdisc = rtnl_qdisc_get(qdiscCache, Index, Handle); if (!qdisc) { result = Kind == ""; goto out; } nl.Dump("found", qdisc); if (rtnl_tc_get_ifindex(TC_CAST(qdisc)) != Index) goto out; if (rtnl_tc_get_parent(TC_CAST(qdisc)) != Parent) goto out; if (rtnl_tc_get_handle(TC_CAST(qdisc)) != Handle) goto out; if (rtnl_tc_get_kind(TC_CAST(qdisc)) != Kind) goto out; if (Kind == "htb") { if (rtnl_htb_get_defcls(qdisc) != Default) goto out; } result = true; out: rtnl_qdisc_put(qdisc); nl_cache_free(qdiscCache); return result; }
static int init_qdisc(libxl__checkpoint_devices_state *cds, libxl__remus_device_nic *remus_nic) { int rc, ret, ifindex; struct rtnl_link *ifb = NULL; struct rtnl_qdisc *qdisc = NULL; libxl__remus_state *rs = cds->concrete_data; STATE_AO_GC(cds->ao); /* Now that we have brought up REMUS_IFB device with plug qdisc for * this vif, so we need to refill the qdisc cache. */ ret = nl_cache_refill(rs->nlsock, rs->qdisc_cache); if (ret) { LOGD(ERROR, cds->domid, "cannot refill qdisc cache: %s", nl_geterror(ret)); rc = ERROR_FAIL; goto out; } /* get a handle to the REMUS_IFB interface */ ret = rtnl_link_get_kernel(rs->nlsock, 0, remus_nic->ifb, &ifb); if (ret) { LOGD(ERROR, cds->domid, "cannot obtain handle for %s: %s", remus_nic->ifb, nl_geterror(ret)); rc = ERROR_FAIL; goto out; } ifindex = rtnl_link_get_ifindex(ifb); if (!ifindex) { LOGD(ERROR, cds->domid, "interface %s has no index", remus_nic->ifb); rc = ERROR_FAIL; goto out; } /* Get a reference to the root qdisc installed on the REMUS_IFB, by * querying the qdisc list we obtained earlier. The netbufscript * sets up the plug qdisc as the root qdisc, so we don't have to * search the entire qdisc tree on the REMUS_IFB dev. * There is no need to explicitly free this qdisc as its just a * reference from the qdisc cache we allocated earlier. */ qdisc = rtnl_qdisc_get_by_parent(rs->qdisc_cache, ifindex, TC_H_ROOT); if (qdisc) { const char *tc_kind = rtnl_tc_get_kind(TC_CAST(qdisc)); /* Sanity check: Ensure that the root qdisc is a plug qdisc. */ if (!tc_kind || strcmp(tc_kind, "plug")) { LOGD(ERROR, cds->domid, "plug qdisc is not installed on %s", remus_nic->ifb); rc = ERROR_FAIL; goto out; } remus_nic->qdisc = qdisc; } else { LOGD(ERROR, cds->domid, "Cannot get qdisc handle from ifb %s", remus_nic->ifb); rc = ERROR_FAIL; goto out; } rc = 0; out: if (ifb) rtnl_link_put(ifb); if (rc && qdisc) nl_object_put((struct nl_object *)qdisc); return rc; }
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); }
Result<icmp::Classifier> decode<icmp::Classifier>( const Netlink<struct rtnl_cls>& cls) { if (rtnl_cls_get_protocol(cls.get()) != ETH_P_IP || rtnl_tc_get_kind(TC_CAST(cls.get())) != string("u32")) { return None(); } // Raw values. Option<uint32_t> protocol; Option<net::IP> destinationIP; // There are at most 0xff keys. for (uint8_t i = 0; i <= 0xff; i++) { uint32_t value; uint32_t mask; int offset; int offsetmask; // Decode a selector from the libnl filter 'cls'. int error = rtnl_u32_get_key( cls.get(), i, &value, &mask, &offset, &offsetmask); if (error != 0) { if (error == -NLE_INVAL) { // This is the case where cls does not have a u32 selector. In // that case, we just return None. return None(); } else if (error == -NLE_RANGE) { break; } else { return Error( "Failed to decode a u32 selector: " + string(nl_geterror(error))); } } // The function "rtnl_u32_get_key" sets value and mask in network // order. Convert them back to host order. value = ntohl(value); mask = ntohl(mask); // IP protocol field. if (offset == 8 && value == 0x00010000 && mask == 0x00ff0000) { protocol = value; } // Destination IP address. if (offset == 16 && mask == 0xffffffff) { destinationIP = net::IP(value); } } if (protocol.isSome()) { return icmp::Classifier(destinationIP); } return None(); }