TError TNl::ProxyNeighbour(int ifindex, const TNlAddr &addr, bool add) { struct rtnl_neigh *neigh; int ret; neigh = rtnl_neigh_alloc(); if (!neigh) return TError(EError::Unknown, "Cannot allocate neighbour"); ret = rtnl_neigh_set_dst(neigh, addr.Addr); if (ret) { rtnl_neigh_put(neigh); return Error(ret, "Cannot set neighbour dst"); } rtnl_neigh_set_flags(neigh, NTF_PROXY); rtnl_neigh_set_state(neigh, NUD_PERMANENT); rtnl_neigh_set_ifindex(neigh, ifindex); if (add) { Dump("add", neigh); ret = rtnl_neigh_add(Sock, neigh, NLM_F_CREATE | NLM_F_REPLACE); } else { Dump("del", neigh); ret = rtnl_neigh_delete(Sock, neigh, 0); if (ret == -NLE_OBJ_NOTFOUND) ret = 0; } rtnl_neigh_put(neigh); if (ret) return Error(ret, "Cannot modify neighbour for l3 network"); return TError::Success(); }
static struct rtnl_neigh *create_filter_neigh_for_dst(struct nl_addr *dst_addr, int oif) { struct rtnl_neigh *filter_neigh; filter_neigh = rtnl_neigh_alloc(); if (filter_neigh == NULL) return NULL; rtnl_neigh_set_ifindex(filter_neigh, oif); rtnl_neigh_set_dst(filter_neigh, dst_addr); return filter_neigh; }
void switchlink_linux_mac_update(switchlink_mac_addr_t mac_addr, switchlink_handle_t bridge_h, switchlink_handle_t intf_h, bool create) { switchlink_db_status_t status; uint32_t ifindex; if (!create) { status = switchlink_db_mac_get_intf(mac_addr, bridge_h, &intf_h); if (status != SWITCHLINK_DB_STATUS_SUCCESS) { assert(false); return; } } status = switchlink_db_interface_get_ifindex(intf_h, &ifindex); if (status != SWITCHLINK_DB_STATUS_SUCCESS) { assert(false); return; } struct nl_sock *nlsk = switchlink_get_nl_sock(); if (!nlsk) { return; } struct nl_addr *nl_addr = nl_addr_build(AF_LLC, mac_addr, ETH_ALEN); struct rtnl_neigh *rtnl_neigh = rtnl_neigh_alloc(); rtnl_neigh_set_ifindex(rtnl_neigh, ifindex); rtnl_neigh_set_lladdr(rtnl_neigh, nl_addr); rtnl_neigh_set_state(rtnl_neigh, rtnl_neigh_str2state("permanent")); rtnl_neigh_set_family(rtnl_neigh, AF_BRIDGE); if (create) { status = switchlink_db_mac_add(mac_addr, bridge_h, intf_h); assert(status == SWITCHLINK_DB_STATUS_SUCCESS); rtnl_neigh_add(nlsk, rtnl_neigh, NLM_F_CREATE|NLM_F_REPLACE); } else { status = switchlink_db_mac_delete(mac_addr, bridge_h); assert(status == SWITCHLINK_DB_STATUS_SUCCESS); rtnl_neigh_delete(nlsk, rtnl_neigh, 0); } rtnl_neigh_put(rtnl_neigh); nl_addr_put(nl_addr); }
int nl_bridge::fdb_timeout(rtnl_link *br_link, uint16_t vid, const rofl::caddress_ll &mac) { int rv = 0; std::unique_ptr<rtnl_neigh, decltype(&rtnl_neigh_put)> n(rtnl_neigh_alloc(), rtnl_neigh_put); std::unique_ptr<nl_addr, decltype(&nl_addr_put)> h_src( nl_addr_build(AF_LLC, mac.somem(), mac.memlen()), nl_addr_put); rtnl_neigh_set_ifindex(n.get(), rtnl_link_get_ifindex(br_link)); rtnl_neigh_set_master(n.get(), rtnl_link_get_master(br_link)); rtnl_neigh_set_family(n.get(), AF_BRIDGE); rtnl_neigh_set_vlan(n.get(), vid); rtnl_neigh_set_lladdr(n.get(), h_src.get()); rtnl_neigh_set_flags(n.get(), NTF_MASTER | NTF_EXT_LEARNED); rtnl_neigh_set_state(n.get(), NUD_REACHABLE); // find entry in local l2_cache std::unique_ptr<rtnl_neigh, decltype(&rtnl_neigh_put)> n_lookup( NEIGH_CAST(nl_cache_search(l2_cache.get(), OBJ_CAST(n.get()))), rtnl_neigh_put); if (n_lookup) { // * remove l2 entry from kernel nl_msg *msg = nullptr; rtnl_neigh_build_delete_request(n.get(), NLM_F_REQUEST, &msg); assert(msg); // send the message and create new fdb entry if (nl->send_nl_msg(msg) < 0) { LOG(ERROR) << __FUNCTION__ << ": failed to send netlink message"; return -EINVAL; } // XXX TODO maybe delete after NL event and not yet here nl_cache_remove(OBJ_CAST(n_lookup.get())); } return rv; }
std::deque<rtnl_neigh *> nl_bridge::get_fdb_entries_of_port(rtnl_link *br_port, uint16_t vid) { std::unique_ptr<rtnl_neigh, decltype(&rtnl_neigh_put)> filter( rtnl_neigh_alloc(), rtnl_neigh_put); rtnl_neigh_set_ifindex(filter.get(), rtnl_link_get_ifindex(br_port)); rtnl_neigh_set_master(filter.get(), rtnl_link_get_ifindex(bridge)); rtnl_neigh_set_vlan(filter.get(), vid); rtnl_neigh_set_family(filter.get(), AF_BRIDGE); std::deque<rtnl_neigh *> neighs; nl_cache_foreach_filter( nl->get_cache(cnetlink::NL_NEIGH_CACHE), OBJ_CAST(filter.get()), [](struct nl_object *o, void *arg) { std::deque<rtnl_neigh *> *neighs = (std::deque<rtnl_neigh *> *)arg; neighs->push_back(NEIGH_CAST(o)); VLOG(3) << "needs to be updated " << o; }, &neighs); return neighs; }
static void nl_ihandler_cb(struct incident *i, void *ctx) { g_debug("%s i %p ctx %p", __PRETTY_FUNCTION__, i, ctx); struct connection *con; incident_value_con_get(i, "con", &con); char *remote = con->remote.ip_string; char *local = con->local.ip_string; char *prefix = "::ffff:"; if( strncmp(local, prefix, strlen(prefix)) == 0) local += strlen(prefix); if( strncmp(remote, prefix, strlen(prefix)) == 0) remote += strlen(prefix); int ifindex; int err; { g_debug("local addr %s remote addr %s", local, remote); struct rtnl_addr *addr = rtnl_addr_alloc(); struct nl_addr *a; if ( ( err = nl_addr_parse(local, AF_UNSPEC, &a)) != 0 ) g_critical("could not parse addr %s (%s)", local, nl_geterror(err)); rtnl_addr_set_local(addr, a); nl_addr_put(a); struct rtnl_addr *res = NULL; nl_cache_foreach_filter(nl_runtime.addr_cache, OBJ_CAST(addr), cache_lookup_cb, &res); g_critical("LOCAL RTNL_ADDR %p", res); /* struct nl_dump_params params = { .dp_type = NL_DUMP_LINE, .dp_fd = stdout, }; nl_cache_dump_filter(nl_runtime.addr_cache, ¶ms, OBJ_CAST(addr)); */ ifindex = rtnl_addr_get_ifindex(res); } struct rtnl_neigh *res = NULL; { struct rtnl_neigh *neigh = rtnl_neigh_alloc(); rtnl_neigh_set_ifindex(neigh, ifindex); struct nl_addr *a; if ( ( err = nl_addr_parse(remote, AF_UNSPEC, &a)) != 0 ) g_critical("could not parse addr %s (%s)", remote, nl_geterror(err)); rtnl_neigh_set_dst(neigh, a); nl_addr_put(a); nl_cache_foreach_filter(nl_runtime.neigh_cache, OBJ_CAST(neigh), cache_lookup_cb, &res); } if( res ) { g_critical("GOT NEIGH %p", res); struct nl_addr *lladdr = rtnl_neigh_get_lladdr(res); char buf[123]; nl_addr2str(lladdr, buf, sizeof(buf)); g_critical("GOT NEIGH %s", buf); struct incident *i = incident_new("dionaea.module.nl.connection.info.mac"); incident_value_string_set(i, "mac", g_string_new(buf)); incident_value_con_set(i, "con", con); incident_report(i); incident_free(i); } }
int nl_bridge::learn_source_mac(rtnl_link *br_link, packet *p) { // we still assume vlan filtering bridge assert(rtnl_link_get_family(br_link) == AF_BRIDGE); VLOG(2) << __FUNCTION__ << ": pkt " << p << " on link " << OBJ_CAST(br_link); rtnl_link_bridge_vlan *br_vlan = rtnl_link_bridge_get_port_vlan(br_link); if (br_vlan == nullptr) { LOG(ERROR) << __FUNCTION__ << ": only the vlan filtering bridge is supported"; return -EINVAL; } // parse ether frame and check for vid vlan_hdr *hdr = reinterpret_cast<basebox::vlan_hdr *>(p->data); uint16_t vid = 0; // XXX TODO maybe move this to the utils to have a std lib for parsing the // ether frame switch (ntohs(hdr->eth.h_proto)) { case ETH_P_8021Q: // vid vid = ntohs(hdr->vlan); break; default: // no vid, set vid to pvid vid = br_vlan->pvid; LOG(WARNING) << __FUNCTION__ << ": assuming untagged for ethertype " << std::showbase << std::hex << ntohs(hdr->eth.h_proto); break; } // verify that the vid is in use here if (!is_vid_set(vid, br_vlan->vlan_bitmap)) { LOG(WARNING) << __FUNCTION__ << ": got packet on unconfigured port"; return -ENOTSUP; } // set nl neighbour to NL std::unique_ptr<nl_addr, decltype(&nl_addr_put)> h_src( nl_addr_build(AF_LLC, hdr->eth.h_source, sizeof(hdr->eth.h_source)), nl_addr_put); if (!h_src) { LOG(ERROR) << __FUNCTION__ << ": failed to allocate src mac"; return -ENOMEM; } std::unique_ptr<rtnl_neigh, decltype(&rtnl_neigh_put)> n(rtnl_neigh_alloc(), rtnl_neigh_put); rtnl_neigh_set_ifindex(n.get(), rtnl_link_get_ifindex(br_link)); rtnl_neigh_set_master(n.get(), rtnl_link_get_master(br_link)); rtnl_neigh_set_family(n.get(), AF_BRIDGE); rtnl_neigh_set_vlan(n.get(), vid); rtnl_neigh_set_lladdr(n.get(), h_src.get()); rtnl_neigh_set_flags(n.get(), NTF_MASTER | NTF_EXT_LEARNED); rtnl_neigh_set_state(n.get(), NUD_REACHABLE); // check if entry already exists in cache if (is_mac_in_l2_cache(n.get())) { return 0; } nl_msg *msg = nullptr; rtnl_neigh_build_add_request(n.get(), NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, &msg); assert(msg); // send the message and create new fdb entry if (nl->send_nl_msg(msg) < 0) { LOG(ERROR) << __FUNCTION__ << ": failed to send netlink message"; return -EINVAL; } // cache the entry if (nl_cache_add(l2_cache.get(), OBJ_CAST(n.get())) < 0) { LOG(ERROR) << __FUNCTION__ << ": failed to add entry to l2_cache " << OBJ_CAST(n.get()); return -EINVAL; } VLOG(2) << __FUNCTION__ << ": learned new source mac " << OBJ_CAST(n.get()); return 0; }
void nl_bridge::update_vlans(rtnl_link *old_link, rtnl_link *new_link) { assert(sw); assert(bridge); // already checked rtnl_link_bridge_vlan *old_br_vlan, *new_br_vlan; rtnl_link *_link; if (old_link == nullptr) { // link added old_br_vlan = &empty_br_vlan; new_br_vlan = rtnl_link_bridge_get_port_vlan(new_link); _link = nl->get_link(rtnl_link_get_ifindex(new_link), AF_UNSPEC); } else if (new_link == nullptr) { // link deleted old_br_vlan = rtnl_link_bridge_get_port_vlan(old_link); new_br_vlan = &empty_br_vlan; _link = nl->get_link(rtnl_link_get_ifindex(old_link), AF_UNSPEC); } else { // link updated old_br_vlan = rtnl_link_bridge_get_port_vlan(old_link); new_br_vlan = rtnl_link_bridge_get_port_vlan(new_link); _link = nl->get_link(rtnl_link_get_ifindex(new_link), AF_UNSPEC); } if (old_br_vlan == nullptr) { old_br_vlan = &empty_br_vlan; } if (new_br_vlan == nullptr) { new_br_vlan = &empty_br_vlan; } if (_link == nullptr) { // XXX FIXME in case a vxlan has been deleted the vxlan_domain and // vxlan_dom_bitmap need an update, maybe this can be handled already from // the link_deleted of the vxlan itself? LOG(WARNING) << __FUNCTION__ << ": could not get parent link of bridge interface. This " "case needs further checks if everything got already " "deleted."; return; } // check for vid changes if (br_vlan_equal(old_br_vlan, new_br_vlan)) { VLOG(2) << __FUNCTION__ << ": vlans did not change"; return; } link_type lt = get_link_type(_link); uint32_t pport_no = 0; uint32_t tunnel_id = -1; std::deque<rtnl_link *> bridge_ports; if (lt == LT_VXLAN) { assert(nl); nl->get_bridge_ports(rtnl_link_get_master(_link), &bridge_ports); if (vxlan->get_tunnel_id(_link, nullptr, &tunnel_id) != 0) { LOG(ERROR) << __FUNCTION__ << ": failed to get vni of link " << OBJ_CAST(_link); } } else { pport_no = nl->get_port_id(rtnl_link_get_ifindex(_link)); if (pport_no == 0) { LOG(ERROR) << __FUNCTION__ << ": invalid pport_no=0 of link: " << OBJ_CAST(_link); return; } } for (int k = 0; k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; k++) { int base_bit; uint32_t a = old_br_vlan->vlan_bitmap[k]; uint32_t b = new_br_vlan->vlan_bitmap[k]; uint32_t vlan_diff = a ^ b; #if 0 // untagged change not yet implemented uint32_t c = old_br_vlan->untagged_bitmap[k]; uint32_t d = new_br_vlan->untagged_bitmap[k]; uint32_t untagged_diff = c ^ d; #endif // 0 base_bit = k * 32; int i = -1; int done = 0; while (!done) { int j = find_next_bit(i, vlan_diff); if (j > 0) { // vlan added or removed int vid = j - 1 + base_bit; bool egress_untagged = false; // check if egress is untagged if (new_br_vlan->untagged_bitmap[k] & 1 << (j - 1)) { egress_untagged = true; #if 0 // untagged change not yet implemented // clear untagged_diff bit untagged_diff &= ~((uint32_t)1 << (j - 1)); #endif // 0 } if (new_br_vlan->vlan_bitmap[k] & 1 << (j - 1)) { // vlan added if (lt == LT_VXLAN) { // update vxlan domain if (!is_vid_set(vid, vxlan_dom_bitmap)) { VLOG(1) << __FUNCTION__ << ": new vxlan domain vid=" << vid << ", tunnel_id=" << tunnel_id; vxlan_domain.emplace(vid, tunnel_id); set_vid(vid, vxlan_dom_bitmap); } else { // XXX TODO check the map } // update all bridge ports to be access ports update_access_ports(_link, new_link ? new_link : old_link, vid, tunnel_id, bridge_ports, true); } else { assert(pport_no); if (is_vid_set(vid, vxlan_dom_bitmap)) { // configure as access port std::string port_name = std::string(rtnl_link_get_name(_link)); auto vxd_it = vxlan_domain.find(vid); if (vxd_it != vxlan_domain.end()) { vxlan->create_access_port((new_link) ? new_link : old_link, vxd_it->second, port_name, pport_no, vid, egress_untagged, nullptr); } else { LOG(FATAL) << __FUNCTION__ << ": should not happen, something is broken"; } } else { // normal vlan port VLOG(3) << __FUNCTION__ << ": add vid=" << vid << " on pport_no=" << pport_no << " link: " << OBJ_CAST(_link); sw->egress_bridge_port_vlan_add(pport_no, vid, egress_untagged); sw->ingress_port_vlan_add(pport_no, vid, new_br_vlan->pvid == vid); } } } else { // vlan removed if (lt == LT_VXLAN) { unset_vid(vid, vxlan_dom_bitmap); vxlan_domain.erase(vid); // update all bridge ports to be normal bridge ports update_access_ports(_link, new_link ? new_link : old_link, vid, tunnel_id, bridge_ports, false); } else { VLOG(3) << __FUNCTION__ << ": remove vid=" << vid << " on pport_no=" << pport_no << " link: " << OBJ_CAST(_link); sw->ingress_port_vlan_remove(pport_no, vid, old_br_vlan->pvid == vid); // delete all FM pointing to this group first sw->l2_addr_remove_all_in_vlan(pport_no, vid); std::unique_ptr<rtnl_neigh, decltype(&rtnl_neigh_put)> filter( rtnl_neigh_alloc(), rtnl_neigh_put); rtnl_neigh_set_ifindex(filter.get(), rtnl_link_get_ifindex(bridge)); rtnl_neigh_set_master(filter.get(), rtnl_link_get_master(bridge)); rtnl_neigh_set_family(filter.get(), AF_BRIDGE); rtnl_neigh_set_vlan(filter.get(), vid); rtnl_neigh_set_flags(filter.get(), NTF_MASTER | NTF_EXT_LEARNED); rtnl_neigh_set_state(filter.get(), NUD_REACHABLE); nl_cache_foreach_filter(l2_cache.get(), OBJ_CAST(filter.get()), [](struct nl_object *o, void *arg) { VLOG(3) << "l2_cache remove object " << o; nl_cache_remove(o); }, nullptr); sw->egress_bridge_port_vlan_remove(pport_no, vid); } } i = j; } else { done = 1; } } #if 0 // not yet implemented the update done = 0; i = -1; while (!done) { // vlan is existing, but swapping egress tagged/untagged int j = find_next_bit(i, untagged_diff); if (j > 0) { // egress untagged changed int vid = j - 1 + base_bit; bool egress_untagged = false; // check if egress is untagged if (new_br_vlan->untagged_bitmap[k] & 1 << (j-1)) { egress_untagged = true; } // XXX implement update fm_driver.update_port_vid_egress(devname, vid, egress_untagged); i = j; } else { done = 1; } } #endif } }