/* * Copyright (c) 2001-2020 Mellanox Technologies, Ltd. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "utils/bullseye.h" #include "utils/lock_wrapper.h" #include "vlogger/vlogger.h" #include "vma/util/vtypes.h" #include "vma/util/utils.h" #include "vma/util/if.h" #include "rule_table_mgr.h" #include "vma/sock/socket_fd_api.h" #include "vma/sock/sock-redirect.h" #include "ip_address.h" // debugging macros #define MODULE_NAME "rrm:" #define rr_mgr_if_logpanic __log_panic #define rr_mgr_logerr __log_err #define rr_mgr_logwarn __log_warn #define rr_mgr_loginfo __log_info #define rr_mgr_logdbg __log_dbg #define rr_mgr_logfunc __log_func #define rr_mgr_logfuncall __log_funcall rule_table_mgr* g_p_rule_table_mgr = NULL; rule_table_mgr::rule_table_mgr() : netlink_socket_mgr(RULE_DATA_TYPE), cache_table_mgr*>("rule_table_mgr") { rr_mgr_logdbg(""); //Read Rule table from kernel and save it in local variable. update_tbl(); //Print table print_val_tbl(); rr_mgr_logdbg("Done"); } //This function uses Netlink to get routing rules saved in kernel then saved it locally. void rule_table_mgr::update_tbl() { auto_unlocker lock(m_lock); netlink_socket_mgr::update_tbl(); return; } // Parse received rule entry into custom object (rule_val). // Parameters: // nl_header : object that contain rule entry. // p_val : custom object that contain parsed rule data. // return true if its not related to local or default table, false otherwise. bool rule_table_mgr::parse_enrty(nlmsghdr *nl_header, rule_val *p_val) { int len; struct rtmsg *rt_msg; struct rtattr *rt_attribute; // get rule entry header rt_msg = (struct rtmsg *) NLMSG_DATA(nl_header); // we are not concerned about the local and default rule table if (rt_msg->rtm_family != AF_INET || rt_msg->rtm_table == RT_TABLE_LOCAL) return false; p_val->set_protocol(rt_msg->rtm_protocol); p_val->set_scope(rt_msg->rtm_scope); p_val->set_type(rt_msg->rtm_type); p_val->set_tos(rt_msg->rtm_tos); p_val->set_table_id(rt_msg->rtm_table); len = RTM_PAYLOAD(nl_header); rt_attribute = (struct rtattr *) RTM_RTA(rt_msg); for (;RTA_OK(rt_attribute, len);rt_attribute=RTA_NEXT(rt_attribute,len)) { parse_attr(rt_attribute, p_val); } p_val->set_state(true); p_val->set_str(); return true; } // Parse received rule attribute for given rule. // Parameters: // rt_attribute : object that contain rule attribute. // p_val : custom object that contain parsed rule data. void rule_table_mgr::parse_attr(struct rtattr *rt_attribute, rule_val *p_val) { switch (rt_attribute->rta_type) { case FRA_PRIORITY: p_val->set_priority(*(uint32_t *)RTA_DATA(rt_attribute)); break; case FRA_DST: p_val->set_dst_addr(*(in_addr_t *)RTA_DATA(rt_attribute)); break; case FRA_SRC: p_val->set_src_addr(*(in_addr_t *)RTA_DATA(rt_attribute)); break; case FRA_IFNAME: p_val->set_iif_name((char *)RTA_DATA(rt_attribute)); break; case FRA_TABLE: p_val->set_table_id(*(uint32_t *)RTA_DATA(rt_attribute)); break; #if DEFINED_FRA_OIFNAME case FRA_OIFNAME: p_val->set_oif_name((char *)RTA_DATA(rt_attribute)); break; #endif default: rr_mgr_logdbg("got undetected rta_type %d %x", rt_attribute->rta_type, *(uint32_t *)RTA_DATA(rt_attribute)); break; } } // Create rule entry object for given destination key and fill it with matching rule value from rule table. // Parameters: // key : key object that contain information about destination. // obs : object that contain observer for specific rule entry. // Returns created rule entry object. rule_entry* rule_table_mgr::create_new_entry(route_rule_table_key key, const observer *obs) { rr_mgr_logdbg(""); NOT_IN_USE(obs); rule_entry* p_ent = new rule_entry(key); update_entry(p_ent); rr_mgr_logdbg("new entry %p created successfully", p_ent); return p_ent; } // Update invalid rule entry with matching rule value from rule table. // Parameters: // p_ent : rule entry that will be updated if it is invalid. void rule_table_mgr::update_entry(rule_entry* p_ent) { rr_mgr_logdbg("entry [%p]", p_ent); auto_unlocker lock(m_lock); if (p_ent && !p_ent->is_valid()) { //if entry is found in the collection and is not valid rr_mgr_logdbg("rule_entry is not valid-> update value"); std::deque* p_rrv; p_ent->get_val(p_rrv); /* p_rrv->clear(); TODO for future rule live updates */ if (!find_rule_val(p_ent->get_key(), p_rrv)) { rr_mgr_logdbg("ERROR: could not find rule val for rule_entry '%s'", p_ent->to_str().c_str()); } } } // Find rule form rule table that match given destination info. // Parameters: // key : key object that contain information about destination. // p_val : list of rule_val object that will contain information about all rule that match destination info // Returns true if at least one rule match destination info, false otherwise. bool rule_table_mgr::find_rule_val(route_rule_table_key key, std::deque* &p_val) { rr_mgr_logfunc("destination info %s:", key.to_str().c_str()); for (int index = 0; index < m_tab.entries_num; index++) { rule_val* p_val_from_tbl = &m_tab.value[index]; if (p_val_from_tbl->is_valid() && is_matching_rule(key, p_val_from_tbl)) { p_val->push_back(p_val_from_tbl); rr_mgr_logdbg("found rule val[%p]: %s", p_val_from_tbl, p_val_from_tbl->to_str()); } } return !p_val->empty(); } // Check matching between given destination info. and specific rule from rule table. // Parameters: // key : key object that contain information about destination. // p_val : rule_val object that contain information about specific rule from rule table // Returns true if destination info match rule value, false otherwise. bool rule_table_mgr::is_matching_rule(route_rule_table_key key, rule_val* p_val) { in_addr_t m_dst_ip = key.get_dst_ip(); in_addr_t m_src_ip = key.get_src_ip(); uint8_t m_tos = key.get_tos(); in_addr_t rule_dst_ip = p_val->get_dst_addr(); in_addr_t rule_src_ip = p_val->get_src_addr(); uint8_t rule_tos = p_val->get_tos(); char* rule_iif_name = (char *)p_val->get_iif_name(); char* rule_oif_name = (char *)p_val->get_oif_name(); bool is_match = false; // Only destination IP, source IP and TOS are checked with rule, since IIF and OIF is not filled in dst_entry object. if ((rule_dst_ip == 0) || (rule_dst_ip == m_dst_ip)) { // Check match in destination IP if ((rule_src_ip == 0) || (rule_src_ip == m_src_ip)) { // Check match in source IP if ((rule_tos == 0) || (rule_tos == m_tos)) { // Check match in TOS value if (strcmp(rule_iif_name, "") == 0) { // Check that rule doesn't contain IIF since we can't check match with if (strcmp(rule_oif_name, "") == 0) { // Check that rule doesn't contain OIF since we can't check match with is_match = true; } } } } } return is_match; } // Find table ID for given destination info. // Parameters: // key : key object that contain information about destination. // table_id_list : list that will contain table ID for all rule that match destination info // Returns true if at least one rule match destination info, false otherwise. bool rule_table_mgr::rule_resolve(route_rule_table_key key, std::deque &table_id_list) { rr_mgr_logdbg("dst info: '%s'", key.to_str().c_str()); std::deque values; std::deque* p_values = &values; auto_unlocker lock(m_lock); if (find_rule_val(key, p_values)) { for (std::deque::iterator val = values.begin(); val != values.end(); val++) { table_id_list.push_back((*val)->get_table_id()); rr_mgr_logdbg("dst info: '%s' resolved to table ID '%u'", key.to_str().c_str(), (*val)->get_table_id()); } } return !table_id_list.empty(); }