static struct nl_msg * build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags) { struct nl_msg *m; struct nlmsghdr n = { .nlmsg_type = cmd, .nlmsg_flags = flags, }; struct ndmsg nm = { .ndm_ifindex = tmpl->n_ifindex, .ndm_family = tmpl->n_dst.a_family, .ndm_state = tmpl->n_state, }; m = nl_msg_build(&n); nl_msg_append_raw(m, &nm, sizeof(nm)); nl_msg_append_tlv(m, NDA_DST, &tmpl->n_dst.a_addr, tmpl->n_dst.a_len); if (tmpl->n_mask & NEIGH_HAS_LLADDR) nl_msg_append_tlv(m, NDA_LLADDR, &tmpl->n_lladdr.a_addr, tmpl->n_lladdr.a_len); return m; } /** * Build netlink request message to add a new neighbour * @arg tmpl template with data of new neighbour * * Builds a new netlink message requesting a addition of a new * neighbour. The netlink message header isn't fully equipped with * all relevant fields and must thus be sent out via nl_send_auto_complete() * or supplemented as needed. \a tmpl must contain the attributes of the new * neighbour set via \c rtnl_neigh_set_* functions. * * The following attributes must be set in the template: * - Interface index (rtnl_neigh_set_ifindex()) * - State (rtnl_neigh_set_state()) * - Destination address (rtnl_neigh_set_dst()) * - Link layer address (rtnl_neigh_set_lladdr()) * * @return The netlink message */ struct nl_msg * rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl) { return build_neigh_msg(tmpl, RTM_NEWNEIGH, NLM_F_CREATE); } /** * Add a new neighbour * @arg handle netlink handle * @arg tmpl template with requested changes * * Builds a netlink message by calling rtnl_neigh_build_add_request(), * sends the request to the kernel and waits for the next ACK to be * received and thus blocks until the request has been fullfilled. * * The following attributes must be set in the template: * - Interface index (rtnl_neigh_set_ifindex()) * - State (rtnl_neigh_set_state()) * - Destination address (rtnl_neigh_set_dst()) * - Link layer address (rtnl_neigh_set_lladdr()) * * @return 0 on sucess or a negative error if an error occured. */ int rtnl_neigh_add(struct nl_handle *handle, struct rtnl_neigh *tmpl) { int err; struct nl_msg *m = rtnl_neigh_build_add_request(tmpl); if ((err = nl_send_auto_complete(handle, nl_msg_get(m))) < 0) return err; nl_msg_free(m); return nl_wait_for_ack(handle); }
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; }