/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipfirewall_mac.c,v $ $Revision: 1.35.16.2 $ * $Source: /home/interpeak/CVSRoot/ipfirewall/src/ipfirewall_mac.c,v $ * $Author: svc-cmnet $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Markus Carlstedt ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * MAC filter. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipfirewall_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #ifdef IPFIREWALL_USE_MAC_FILTER #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #define IPFIREWALL_C #include "ipfirewall.h" #include "ipfirewall_h.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* *=========================================================================== * IPFIREWALL_STATS *=========================================================================== * MAC Filter statistics counters */ #ifdef IPFIREWALL_USE_MAC_STATISTICS #define IPFIREWALL_STATS(xentry) ipfirewall->inner.stats.xentry #else #define IPFIREWALL_STATS(xentry) IP_NOOP #endif /* *=========================================================================== * IPFIREWALL_MAC_IS_ME *=========================================================================== * Check if MAC frame address is local */ #define IPFIREWALL_MAC_IS_ME(pktaddr, netif) \ (netif->link_addr_size == IPFIREWALL_MAC_ADDRESS_LEN && \ ((Ip_u16 *)pktaddr)[0] == ((Ip_u16 *)netif->link_addr)[0] && \ ((Ip_u16 *)pktaddr)[1] == ((Ip_u16 *)netif->link_addr)[1] && \ ((Ip_u16 *)pktaddr)[2] == ((Ip_u16 *)netif->link_addr)[2]) /* *=========================================================================== * IPFIREWALL_MAC_COMPARE *=========================================================================== * Compare MAC frame address with rule address */ #define IPFIREWALL_MAC_COMPARE(addr, mask, pktaddr) \ (((Ip_u16 *)addr)[0] == (((Ip_u16 *)pktaddr)[0] & ((Ip_u16 *)mask)[0]) && \ ((Ip_u16 *)addr)[1] == (((Ip_u16 *)pktaddr)[1] & ((Ip_u16 *)mask)[1]) && \ ((Ip_u16 *)addr)[2] == (((Ip_u16 *)pktaddr)[2] & ((Ip_u16 *)mask)[2])) /* *=========================================================================== * IPFIREWALL_MAC_TEST_ADRESS *=========================================================================== * Test MAC address against rule */ #define IPFIREWALL_MAC_TEST_ADRESS(any, me, not, addr, mask, pktaddr, netif) \ { \ if (any) \ { \ if (not) \ continue; \ } \ else if (me) \ { \ if (IPFIREWALL_MAC_IS_ME(pktaddr, netif)) \ { \ if (not) \ continue; \ } \ else if (!not) \ continue; \ } \ else \ { \ if (IPFIREWALL_MAC_COMPARE(addr, mask, pktaddr)) \ { \ if (not) \ continue; \ } \ else if (!not) \ continue; \ } \ } /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipfirewall_mac_log_pkt *=========================================================================== * Description: Log a packet that matched a MAC filter rule. * Parameters: fw_rule - pointer to the rule. * netif - pointer to interface. * pkt - pointer to packet. * Returns: nothing. */ IP_STATIC void ipfirewall_mac_log_pkt(Ipfirewall_rule *fw_rule, Ipcom_netif *netif, Ipcom_pkt *pkt) { Ipfirewall_mac_log *fw_mac_log; struct Ip_timeval logtime; if(fw_rule->inner.log_first == 1 && fw_rule->inner.log_hit == 1) { IPCOM_LOG2(DEBUG, "ipfirewall_log_pkt() :: MAC rule @%d:%d has already caused a log entry", fw_rule->inner.group_no, fw_rule->inner.rule_no); return; } if(ipfirewall->mac_head_log.size >= IPFIREWALL_MAX_MAC_LOG_ENTRIES) { IPCOM_LOG0(DEBUG, "ipfirewall_mac_log_pkt() :: Maximum number of log entries reached"); IPFIREWALL_STATS(mac_log_failures++); return; } fw_mac_log = ipcom_malloc(sizeof(*fw_mac_log)); if(fw_mac_log == IP_NULL) { IPCOM_LOG0(ERR, "ipfirewall_mac_log_pkt() :: Could not allocate memory for log entry"); IPFIREWALL_STATS(mac_log_failures++); return; } ipcom_memset(fw_mac_log, 0, sizeof(*fw_mac_log)); /* Populate the log entry */ ipcom_microtime(&logtime); fw_mac_log->inner.tv_sec = (Ip_s32)logtime.tv_sec; fw_mac_log->inner.tv_usec = (Ip_s32)logtime.tv_usec; ipcom_strcpy((char *)fw_mac_log->inner.ifname, (char *)netif->name); fw_mac_log->inner.rule_no = fw_rule->inner.rule_no; fw_mac_log->inner.group_no = fw_rule->inner.group_no; fw_mac_log->inner.block = fw_rule->inner.block; ipcom_memcpy(fw_mac_log->inner.dst, &pkt->data[pkt->start], IPFIREWALL_MAC_ADDRESS_LEN); ipcom_memcpy(fw_mac_log->inner.src, &pkt->data[pkt->start+IPFIREWALL_MAC_ADDRESS_LEN], IPFIREWALL_MAC_ADDRESS_LEN); ipcom_memcpy(&fw_mac_log->inner.type_n, &pkt->data[pkt->start+IPFIREWALL_MAC_ADDRESS_LEN*2], 2); fw_mac_log->inner.length = pkt->end - pkt->start - IPFIREWALL_MAC_HEADER_LEN; ipcom_memcpy(fw_mac_log->inner.frame, &pkt->data[pkt->start + IPFIREWALL_MAC_HEADER_LEN], IP_MIN((Ip_size_t)fw_mac_log->inner.length, sizeof(fw_mac_log->inner.frame))); ipcom_list_insert_last(&ipfirewall->mac_head_log, &fw_mac_log->list_log); IPCOM_LOG2(DEBUG, "ipfirewall_mac_log_pkt() :: Logged packet from MAC rule @%d:%d", fw_rule->inner.group_no, fw_rule->inner.rule_no); if(fw_rule->inner.log_first == 1) fw_rule->inner.log_hit = 1; if(fw_rule->inner.block == 1) { if(fw_rule->inner.in == 1) IPFIREWALL_STATS(mac_in_logged_block++); else IPFIREWALL_STATS(mac_out_logged_block++); } else { if(fw_rule->inner.in == 1) IPFIREWALL_STATS(mac_in_logged_pass++); else IPFIREWALL_STATS(mac_out_logged_pass++); } } /*lint !e429 */ /* *=========================================================================== * ipfirewall_mac_check_rules *=========================================================================== * Description: Check packet against MAC filter rules. * Parameters: in - IP_TRUE for incoming packet. * netif - pointer to interface. * pkt - pointer to packet. * Returns: 1 = Block packet. * 0 = Pass packet. */ IP_STATIC Ip_s32 ipfirewall_mac_check_rules(Ipcom_netif *netif, Ipcom_pkt *pkt, Ip_bool in) { Ipfirewall_rule *fw_rule; Ipfirewall_rule *lastmatch = IP_NULL; int current_group = 0; Ip_u8 quick_head = 0; Ip_u16 mac_type_n; Ip_u8 ssap_n = 0; Ip_u8 dsap_n = 0; ip_assert(pkt); ip_assert(pkt->data); ip_assert(pkt->end > pkt->start); if (in == IP_TRUE) fw_rule = IPCOM_LIST_FIRST(&ipfirewall->mac_head_rule_input); else fw_rule = IPCOM_LIST_FIRST(&ipfirewall->mac_head_rule_output); /* Match packet against mac rules */ for (; fw_rule != IP_NULL; fw_rule = IPCOM_LIST_NEXT(&fw_rule->list_rule)) { if (ipfirewall->inner.mac_num_head_rules) { if (quick_head && current_group != fw_rule->inner.group_no) break; if (fw_rule->inner.group_no == 0) { current_group = 0; quick_head = 0; } } if (current_group == fw_rule->inner.group_no || fw_rule->inner.group_head) { /* Check direction */ if (fw_rule->inner.in) { if (in == IP_FALSE) continue; /* Direction does not match */ } else if (in == IP_TRUE) { continue; /* Direction does not match */ } /* Check interface */ if(fw_rule->inner.on) { #ifdef IPNET_USE_VLAN if (fw_rule->inner.in && netif->type != IP_IFT_L2VLAN && ((Ipnet_netif *)netif)->eth != IP_NULL && (ip_ntohs(*(Ip_u16 *)&pkt->data[pkt->start + IPFIREWALL_MAC_ADDRESS_LEN*2]) == 0x8100 #ifdef IPCOM_USE_HW_VLAN_TAGGING || IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_VLAN_TAG) #endif ) && ((Ipnet_netif *)netif)->eth->vlan.ifs != IP_NULL) { /* Find the correct VLAN interface */ Ipnet_netif *ipnet_netif; int vid; #ifdef IPCOM_USE_HW_VLAN_TAGGING if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_VLAN_TAG)) vid = IPNET_ETH_PKT_GET_VLAN_TAG(pkt) & 0x0fff; else #endif /* IPCOM_USE_HW_VLAN_TAGGING */ vid = IPNET_ETH_VLAN_GET_VID(*(Ip_u16 *)&pkt->data[pkt->start + IPFIREWALL_MAC_HEADER_LEN]); ipnet_netif = ipcom_hash_get(((Ipnet_netif *)netif)->eth->vlan.ifs, &vid); if (ipnet_netif != IP_NULL) netif = &ipnet_netif->ipcom; } #endif #ifdef IPFIREWALL_PHYSICAL_IF_SUPPORT /* Check the physical interface before the logical interface */ if(fw_rule->inner.on_phys && (pkt->physical_netif != IP_NULL) ) { if (fw_rule->inner.physical_ifindex) { /* Fast check */ IPFIREWALL_TEST_INTERFACE(fw_rule->inner.on_not, fw_rule->inner.physical_ifindex, pkt->physical_netif->ifindex) } else { /* Slow check */ if (ipfirewall_match_interface(fw_rule, pkt->physical_netif) == 0) continue; /* Interface does not match */ } } #endif /* Check interface */ if (fw_rule->inner.on) { if (fw_rule->inner.ifindex) { /* Fast check */ IPFIREWALL_TEST_INTERFACE(fw_rule->inner.on_not, fw_rule->inner.ifindex, netif->ifindex) } else { /* Slow check */ if (ipfirewall_match_interface(fw_rule, netif) == 0) continue; /* Interface does not match */ } } } /* Check source and destination address */ if (fw_rule->inner.from) { IPFIREWALL_MAC_TEST_ADRESS(fw_rule->inner.src_any, fw_rule->inner.src_me, fw_rule->inner.src_not, &fw_rule->inner.mac_src, &fw_rule->inner.mac_src_mask, &pkt->data[pkt->start + IPFIREWALL_MAC_ADDRESS_LEN], netif) IPFIREWALL_MAC_TEST_ADRESS(fw_rule->inner.dst_any, fw_rule->inner.dst_me, fw_rule->inner.dst_not, &fw_rule->inner.mac_dst, &fw_rule->inner.mac_dst_mask, &pkt->data[pkt->start], netif) } /* Check MAC type */ if (fw_rule->inner.mac_type) { #ifdef IPCOM_USE_HW_VLAN_TAGGING if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_VLAN_TAG)) mac_type_n = ip_htons(0x8100); else #endif mac_type_n = *(Ip_u16 *)&pkt->data[pkt->start + IPFIREWALL_MAC_ADDRESS_LEN*2]; if (fw_rule->inner.the_mac_type_n != mac_type_n) { continue; /* MAC type does not match */ } } mac_type_n = *(Ip_u16 *)&pkt->data[pkt->start + IPFIREWALL_MAC_ADDRESS_LEN*2]; /* Check for the 802.2 frame type */ if(fw_rule->inner.frame_802_2 == 1) { if(ip_ntohs(mac_type_n) > IPFIREWALL_MAC_802_3_MAX_LEN) continue; /* Not an 802.2 frame */ } if(fw_rule->inner.dsap == 1 || fw_rule->inner.ssap == 1 || fw_rule->inner.snap_vendor == 1 || fw_rule->inner.snap_type == 1) { if(ip_ntohs(mac_type_n) > IPFIREWALL_MAC_802_3_MAX_LEN) continue; /* Not an 802.2 frame */ dsap_n = *(Ip_u8 *)&pkt->data[pkt->start + IPFIREWALL_MAC_HEADER_LEN]; ssap_n = *(Ip_u8 *)&pkt->data[pkt->start + IPFIREWALL_MAC_HEADER_LEN + 1]; } if(fw_rule->inner.dsap == 1) { IPFIREWALL_TEST_SAP(fw_rule->inner.dsap_not, fw_rule->inner.the_dsap_n , dsap_n) } if(fw_rule->inner.ssap == 1) { IPFIREWALL_TEST_SAP(fw_rule->inner.ssap_not, fw_rule->inner.the_ssap_n , ssap_n) } if(fw_rule->inner.snap_vendor == 1) { /* DSAP and SSAP must be 0xaa for a SNAP header * to be present */ if((ssap_n != 0xaa) || (dsap_n != 0xaa)) continue; IPFIREWALL_TEST_SNAP_VENDOR(fw_rule->inner.snap_vendor_not, &fw_rule->inner.the_snap_vendor_n,&pkt->data[pkt->start + IPFIREWALL_MAC_HEADER_LEN + IPFIREWALL_LLC_HEADER_LEN]) } if(fw_rule->inner.snap_type == 1) { Ip_u16 snap_type_n; /* DSAP and SSAP must be 0xaa for a SNAP header * to be present */ if((ssap_n != 0xaa) || (dsap_n != 0xaa)) continue; snap_type_n = *(Ip_u16 *)&pkt->data[pkt->start + IPFIREWALL_MAC_HEADER_LEN + IPFIREWALL_LLC_HEADER_LEN + 3]; IPFIREWALL_TEST_SNAP(fw_rule->inner.snap_type_not, fw_rule->inner.the_snap_type_n , snap_type_n) } /* Packet matched rule - check rate limiting */ if(fw_rule->inner.limit) { if (ipfirewall_compare_limit(fw_rule) == 0) continue; } #ifdef IPFIREWALL_USE_USERDEF /* Call user defined function */ if(fw_rule->inner.userdef && fw_rule->user_match != IP_NULL) { IPCOM_LOG2(DEBUG2, "ipfirewall_mac_check_rules() :: Calling user defined match function id: %s with paramater string: %s", (char *)fw_rule->inner.userdef_id, fw_rule->inner.userdef_param); if (fw_rule->user_match(pkt, fw_rule->userdef_cookie, fw_rule->userdef_info) == 0) continue; } #endif IPCOM_LOG2(DEBUG, "ipfirewall_mac_check_rules() :: Matched MAC rule @%d:%d", fw_rule->inner.group_no, fw_rule->inner.rule_no); lastmatch = fw_rule; if(!fw_rule->inner.group_head) { if (fw_rule->inner.quick) break; } else { current_group = fw_rule->inner.group_no; quick_head = fw_rule->inner.quick; } } } if(lastmatch == IP_NULL) { if(in == IP_TRUE) IPFIREWALL_STATS(mac_in_nomatch++); else IPFIREWALL_STATS(mac_out_nomatch++); #ifdef IPFIREWALL_BLOCK_IF_NO_MAC_MATCH /* Return block code */ return 1; #else /* Return pass code */ return 0; #endif } else { if(lastmatch->inner.log == 1) ipfirewall_mac_log_pkt(lastmatch, netif, pkt); /* Log the packet */ /* Update statistics */ if(lastmatch->inner.block) { if(in == IP_TRUE) IPFIREWALL_STATS(mac_in_block++); else IPFIREWALL_STATS(mac_out_block++); /* Return block code */ return 1; } else { if(in == IP_TRUE) IPFIREWALL_STATS(mac_in_pass++); else IPFIREWALL_STATS(mac_out_pass++); /* Return pass code */ return 0; } } } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipfirewall_mac_internal_get_rule *=========================================================================== * Description: Get MAC filter rule entry. * Parameters: ctrl - pointer to control structure. * lenp - pointer to length of returned data. * in - Rule direction * Returns: 0 = success. * <0 = fail. */ IP_GLOBAL Ip_s32 ipfirewall_mac_internal_get_rule(Ipfirewall_ctrl *ctrl, Ip_socklen_t *lenp, Ip_bool in) { Ipfirewall_rule *fw_rule; Ip_u32 i; /* Get the specified entry */ if (in == IP_TRUE) fw_rule = IPCOM_LIST_FIRST(&ipfirewall->mac_head_rule_input); else fw_rule = IPCOM_LIST_FIRST(&ipfirewall->mac_head_rule_output); for(i=0; iseqno && fw_rule != IP_NULL; i++) fw_rule = IPCOM_LIST_NEXT(&fw_rule->list_rule); /* Check if the entry existed */ if(fw_rule == IP_NULL) { *lenp = 0; return 0; } ipcom_memcpy(&ctrl->type.rule_info, &fw_rule->inner, sizeof(fw_rule->inner)); *lenp = sizeof(*ctrl); return 0; } /* *=========================================================================== * ipfirewall_mac_internal_get_log *=========================================================================== * Description: Get MAC filter log entry. * Parameters: ctrl - pointer to control structure. * lenp - pointer to length of returned data. * Returns: 0 = success. * <0 = fail. */ IP_GLOBAL Ip_s32 ipfirewall_mac_internal_get_log(Ipfirewall_ctrl *ctrl, Ip_socklen_t *lenp) { Ipfirewall_mac_log *fw_mac_log; Ip_u32 i; /* Get the specified entry */ fw_mac_log = IPCOM_LIST_FIRST(&ipfirewall->mac_head_log); for(i=0; iseqno && fw_mac_log != IP_NULL; i++) fw_mac_log = IPCOM_LIST_NEXT(&fw_mac_log->list_log); /* Check if the entry existed */ if(fw_mac_log == IP_NULL) { *lenp = 0; return 0; } ipcom_memcpy(&ctrl->type.mac_log_info, &fw_mac_log->inner, sizeof(fw_mac_log->inner)); *lenp = sizeof(*ctrl); return 0; } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipfirewall_mac_input_hook *=========================================================================== * Description: MAC filter input hook. * Parameters: netif - pointer to interface. * pkt - pointer to packet. * Returns: 1 = Block packet. * 0 = Pass packet. */ IP_PUBLIC Ip_s32 ipfirewall_mac_input_hook(Ipcom_netif *netif, Ipcom_pkt *pkt) { Ip_s32 ret; if (ipfirewall->inner.enabled == 0 || ipfirewall->inner.mac_num_in_rules == 0) return 0; ret = ipfirewall_mac_check_rules(netif, pkt, IP_TRUE); return ret; } /* *=========================================================================== * ipfirewall_mac_output_hook *=========================================================================== * Description: MAC filter output hook. * Parameters: netif - pointer to interface. * pkt - pointer to packet. * Returns: 1 = Block packet. * 0 = Pass packet. */ IP_PUBLIC Ip_s32 ipfirewall_mac_output_hook(Ipcom_netif *netif, Ipcom_pkt *pkt) { Ip_s32 ret; if (ipfirewall->inner.enabled == 0 || ipfirewall->inner.mac_num_out_rules == 0) return 0; ret = ipfirewall_mac_check_rules(netif, pkt, IP_FALSE); return ret; } #else int ipfirewall_mac_empty_file; #endif /* #ifdef IPFIREWALL_USE_MAC_FILTER */ /* **************************************************************************** * END OF FILE **************************************************************************** */