int32 emf_iflist_list(emf_info_t *emfi, emf_cfg_if_list_t *list, uint32 size) { int32 index = 0; emf_iflist_t *ptr; if (emfi == NULL) { EMF_ERROR("Invalid EMF instance handle passed\n"); return (FAILURE); } if (list == NULL) { EMF_ERROR("Invalid buffer input\n"); return (FAILURE); } for (ptr = emfi->iflist_head; ptr != NULL; ptr = ptr->next) { strncpy(list->if_entry[index++].if_name, ptr->if_ptr->name, 16); } /* Update the total number of entries */ list->num_entries = index; return (SUCCESS); }
/* * Description: This function is called during module load time. It * opens communication channel to configure and start EMF. */ static int32 __init emf_module_init(void) { EMF_DEBUG("Loading EMF\n"); /* Allocate EMF global data object */ emf = MALLOC(NULL, sizeof(emf_struct_t)); if (emf == NULL) { EMF_ERROR("Out of memory allocating emf_info\n"); return (FAILURE); } memset(emf, 0, sizeof(emf_struct_t)); /* Create a Netlink socket in kernel-space */ #define NETLINK_EMFC 17 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) emf->nl_sk = netlink_kernel_create(NETLINK_EMFC, 0, emf_netlink_sock_cb, NULL, THIS_MODULE); #else emf->nl_sk = netlink_kernel_create(NETLINK_EMFC, emf_netlink_sock_cb); #endif if (emf->nl_sk == NULL) { EMF_ERROR("Netlink kernel socket create failed\n"); MFREE(NULL, emf, sizeof(emf_struct_t)); return (FAILURE); } emf->lock = OSL_LOCK_CREATE("EMF Instance List"); if (emf->lock == NULL) { EMF_ERROR("EMF instance list lock create failed\n"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) sock_release(emf->nl_sk->sk_socket); #else sock_release(emf->nl_sk->socket); #endif MFREE(NULL, emf, sizeof(emf_struct_t)); return (FAILURE); } /* Call the common code global init function */ if (emfc_module_init() != SUCCESS) { OSL_LOCK_DESTROY(emf->lock); MFREE(NULL, emf, sizeof(emf_struct_t)); return (FAILURE); } EMF_DEBUG("EMF Init done\n"); return (SUCCESS); }
/* * EMFL Packet Counters/Statistics */ static int32 emf_stats_get(char *buf, char **start, off_t offset, int32 size, int32 *eof, void *data) { emf_info_t *emfi = (emf_info_t *)data; emf_cfg_request_t *cfg; emf_stats_t *emfs; struct bcmstrbuf b; ASSERT(emfi); cfg = MALLOC(emfi->osh, sizeof(emf_cfg_request_t)); if (cfg == NULL) { EMF_ERROR("Out of memory allocating emf_cfg_request\n"); return (FAILURE); } cfg->command_id = EMFCFG_CMD_EMF_STATS; cfg->oper_type = EMFCFG_OPER_TYPE_GET; cfg->size = sizeof(emf_stats_t); emfs = (emf_stats_t *)cfg->arg; emfc_cfg_request_process(emfi->emfci, cfg); if (cfg->status != EMFCFG_STATUS_SUCCESS) { EMF_ERROR("Unable to get the EMF stats\n"); MFREE(emfi->osh, cfg, sizeof(emf_cfg_request_t)); return (FAILURE); } bcm_binit(&b, buf, size); bcm_bprintf(&b, "McastDataPkts McastDataFwd McastFlooded " "McastDataSentUp McastDataDropped\n"); bcm_bprintf(&b, "%-15d %-15d %-15d %-15d %d\n", emfs->mcast_data_frames, emfs->mcast_data_fwd, emfs->mcast_data_flooded, emfs->mcast_data_sentup, emfs->mcast_data_dropped); bcm_bprintf(&b, "IgmpPkts IgmpPktsFwd " "IgmpPktsSentUp MFDBCacheHits MFDBCacheMisses\n"); bcm_bprintf(&b, "%-15d %-15d %-15d %-15d %d\n", emfs->igmp_frames, emfs->igmp_frames_fwd, emfs->igmp_frames_sentup, emfs->mfdb_cache_hits, emfs->mfdb_cache_misses); MFREE(emfi->osh, cfg, sizeof(emf_cfg_request_t)); if (b.size == 0) { EMF_ERROR("Input buffer overflow\n"); return (FAILURE); } return (b.buf - b.origbuf); }
static int32 emf_mfdb_list(char *buf, char **start, off_t offset, int32 size, int32 *eof, void *data) { emf_info_t *emfi = (emf_info_t *)data; emf_cfg_request_t *cfg; struct bcmstrbuf b; emf_cfg_mfdb_list_t *list; int32 i; ASSERT(emfi); cfg = MALLOC(emfi->osh, sizeof(emf_cfg_request_t)); if (cfg == NULL) { EMF_ERROR("Out of memory allocating emf_cfg_request\n"); return (FAILURE); } cfg->command_id = EMFCFG_CMD_MFDB_LIST; cfg->oper_type = EMFCFG_OPER_TYPE_GET; cfg->size = sizeof(cfg->arg); list = (emf_cfg_mfdb_list_t *)cfg->arg; emfc_cfg_request_process(emfi->emfci, cfg); if (cfg->status != EMFCFG_STATUS_SUCCESS) { EMF_ERROR("Unable to get the MFDB list\n"); MFREE(emfi->osh, cfg, sizeof(emf_cfg_request_t)); return (FAILURE); } bcm_binit(&b, buf, size); bcm_bprintf(&b, "Group Interface Pkts\n"); for (i = 0; i < list->num_entries; i++) { bcm_bprintf(&b, "%08x ", list->mfdb_entry[i].mgrp_ip); bcm_bprintf(&b, "%-15s", list->mfdb_entry[i].if_name); bcm_bprintf(&b, "%d\n", list->mfdb_entry[i].pkts_fwd); } MFREE(emfi->osh, cfg, sizeof(emf_cfg_request_t)); if (b.size == 0) { EMF_ERROR("Input buffer overflow\n"); return (FAILURE); } return (b.buf - b.origbuf); }
/* * Description: This function is called by Linux kernel when user * applications sends a message on netlink socket. It * dequeues the message, calls the functions to process * the commands and sends the result back to user. * * Input: skb - Kernel socket structure */ static void emf_netlink_sock_cb(struct sk_buff *skb) { struct nlmsghdr *nlh; nlh = nlmsg_hdr(skb); EMF_DEBUG("Length of the command buffer %d\n", nlh->nlmsg_len); /* Check the buffer for min size */ if (skb->len < NLMSG_SPACE(0) || skb->len < nlh->nlmsg_len || nlh->nlmsg_len < NLMSG_LENGTH(sizeof(emf_cfg_request_t))) { EMF_ERROR("Configuration request size not > %d\n", sizeof(emf_cfg_request_t)); return; } skb = skb_clone(skb, GFP_KERNEL); if (skb == NULL) return; nlh = nlmsg_hdr(skb); /* Process the message */ emf_cfg_request_process((emf_cfg_request_t *)NLMSG_DATA(nlh)); /* Send the result to user process */ NETLINK_CB(skb).pid = nlh->nlmsg_pid; NETLINK_CB(skb).dst_group = 0; netlink_unicast(emf->nl_sk, skb, nlh->nlmsg_pid, MSG_DONTWAIT); }
int32 emf_hooks_register(emf_info_t *emfi) { int32 i, ret, j; if (emf->hooks_reg) return (SUCCESS); /* Register at Netfilter bridge pre-routing and ip post-routing * hooks to capture and process the packets. */ for (i = 0; i < sizeof(emf_nf_ops)/sizeof(struct nf_hook_ops); i++) { ret = nf_register_hook(&emf_nf_ops[i]); if (ret < 0) { EMF_ERROR("Unable to register netfilter hooks\n"); for (j = 0; j < i; j++) nf_unregister_hook(&emf_nf_ops[j]); return (FAILURE); } } emf->hooks_reg++; return (SUCCESS); }
static int32 emf_iflist_add(emf_struct_t *emf, emf_info_t *emfi, struct net_device *if_ptr) { emf_iflist_t *ptr, *prev; OSL_LOCK(emf->lock); if (emf_iflist_find(emf, emfi, if_ptr, &prev) == NULL) { ptr = MALLOC(emfi->osh, sizeof(emf_iflist_t)); if (ptr == NULL) { EMF_ERROR("Unable to allocate iflist entry\n"); OSL_UNLOCK(emf->lock); return (FAILURE); } /* Initialize the iflist entry */ ptr->if_ptr = if_ptr; /* Add the entry to iflist for this EMF instance */ ptr->next = emfi->iflist_head; emfi->iflist_head = ptr; OSL_UNLOCK(emf->lock); return (SUCCESS); } OSL_UNLOCK(emf->lock); return (FAILURE); }
/* * Description: This function is called when user disables EMF on a bridge * interface. It unregisters the Netfilter hook functions, * calls the common code cleanup routine and releases all the * resources allocated during instance creation. * * Input: emf - EMF module global data pointer * emf_info - EMF instance data pointer */ static int32 emf_instance_del(emf_struct_t *emf, emf_info_t *emfi) { bool found = FALSE; osl_t *osh; emf_info_t *ptr, *prev; #ifdef CONFIG_PROC_FS uint8 proc_name[64]; #endif /* CONFIG_PROC_FS */ /* Interfaces attached to the EMF instance should be deleted first */ emf_iflist_clear(emf, emfi); /* Delete the EMF instance */ prev = NULL; for (ptr = emf->list_head; ptr != NULL; prev = ptr, ptr = ptr->next) { if (ptr == emfi) { found = TRUE; if (prev != NULL) prev->next = ptr->next; else emf->list_head = NULL; break; } } if (!found) { EMF_ERROR("EMF instance not found\n"); return (FAILURE); } emf->inst_count--; /* Free the EMF instance */ OSL_UNLOCK(emf->lock); emf_hooks_unregister(ptr); OSL_LOCK(emf->lock); emfc_exit(ptr->emfci); #ifdef CONFIG_PROC_FS sprintf(proc_name, "net/emf_stats_%s", emfi->inst_id); remove_proc_entry(proc_name, 0); sprintf(proc_name, "net/emfdb_%s", emfi->inst_id); remove_proc_entry(proc_name, 0); #endif /* CONFIG_PROC_FS */ osh = ptr->osh; MFREE(emfi->osh, ptr, sizeof(emf_info_t)); osl_detach(osh); return (SUCCESS); }
/* * Description: This function is called by Linux kernel when user * applications sends a message on netlink socket. It * dequeues the message, calls the functions to process * the commands and sends the result back to user. * * Input: sk - Kernel socket structure * len - Length of the message received from user app. */ static void emf_netlink_sock_cb(struct sock *sk, int32 len) { struct sk_buff *skb; struct nlmsghdr *nlh = NULL; uint8 *data = NULL; EMF_DEBUG("Length of the command buffer %d\n", len); /* Dequeue the message from netlink socket */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) #else while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) #endif { /* Check the buffer for min size */ if (skb->len < sizeof(emf_cfg_request_t)) { EMF_ERROR("Configuration request size not > %d\n", sizeof(emf_cfg_request_t)); return; } /* Buffer contains netlink header followed by data */ nlh = (struct nlmsghdr *)skb->data; data = NLMSG_DATA(nlh); /* Process the message */ emf_cfg_request_process((emf_cfg_request_t *)data); /* Send the result to user process */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) NETLINK_CB(skb).pid = nlh->nlmsg_pid; NETLINK_CB(skb).dst_group = 0; #else NETLINK_CB(skb).groups = 0; NETLINK_CB(skb).pid = 0; NETLINK_CB(skb).dst_groups = 0; NETLINK_CB(skb).dst_pid = nlh->nlmsg_pid; #endif netlink_unicast(emf->nl_sk, skb, nlh->nlmsg_pid, MSG_DONTWAIT); } return; }
static void * emf_if_name_validate(uint8 *if_name) { struct net_device *dev; /* Get the interface pointer */ dev = dev_get_by_name(if_name); if (dev == NULL) { EMF_ERROR("Interface %s doesn't exist\n", if_name); return (NULL); } dev_put(dev); return (dev); }
void emf_hooks_unregister(emf_info_t *emfi) { int32 i; if (emf->hooks_reg == 0) { EMF_ERROR("Hooks already unregistered\n"); return; } emf->hooks_reg--; /* Unregister all the hooks */ for (i = 0; i < sizeof(emf_nf_ops)/sizeof(struct nf_hook_ops); i++) { nf_unregister_hook(&emf_nf_ops[i]); } return; }
static void * emf_if_name_validate(uint8 *if_name) { struct net_device *dev; /* Get the interface pointer */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) dev = dev_get_by_name(if_name); #else dev = dev_get_by_name(&init_net, if_name); #endif if (dev == NULL) { EMF_ERROR("Interface %s doesn't exist\n", if_name); return (NULL); } dev_put(dev); return (dev); }
/* * Description: This function is called when the user application enables * EMF on a bridge interface. It primarily allocates memory * for instance data and calls the common code init function. * * Input: emf - EMF module global data pointer * inst_id - EMF instance name * br_ptr - Bridge device pointer */ static emf_info_t * emf_instance_add(emf_struct_t *emf, int8 *inst_id, struct net_device *br_ptr) { emf_info_t *emfi; osl_t *osh; #ifdef CONFIG_PROC_FS uint8 proc_name[64]; #endif /* CONFIG_PROC_FS */ emfc_wrapper_t emfl; if (emf->inst_count > EMF_MAX_INST) { EMF_ERROR("Max instance limit %d exceeded\n", EMF_MAX_INST); return (NULL); } emf->inst_count++; EMF_INFO("Creating EMF instance for %s\n", inst_id); osh = osl_attach(NULL, PCI_BUS, FALSE); ASSERT(osh); /* Allocate os specfic EMF info object */ emfi = MALLOC(osh, sizeof(emf_info_t)); if (emfi == NULL) { EMF_ERROR("Out of memory allocating emf_info\n"); osl_detach(osh); return (NULL); } emfi->osh = osh; /* Save the EMF instance identifier */ strncpy(emfi->inst_id, inst_id, IFNAMSIZ); emfi->inst_id[IFNAMSIZ - 1] = 0; /* Save the device pointer */ emfi->br_dev = br_ptr; /* Fill the linux wrapper specific functions */ emfl.forward_fn = (forward_fn_ptr)emf_forward; emfl.sendup_fn = (sendup_fn_ptr)emf_sendup; emfl.hooks_register_fn = (hooks_register_fn_ptr)emf_hooks_register; emfl.hooks_unregister_fn = (hooks_unregister_fn_ptr)emf_hooks_unregister; /* Initialize EMFC instance */ if ((emfi->emfci = emfc_init(inst_id, (void *)emfi, osh, &emfl)) == NULL) { EMF_ERROR("EMFC init failed\n"); MFREE(osh, emfi, sizeof(emf_info_t)); osl_detach(osh); return (NULL); } EMF_INFO("Created EMFC instance for %s\n", inst_id); /* Initialize the iflist head */ emfi->iflist_head = NULL; #ifdef CONFIG_PROC_FS sprintf(proc_name, "net/emf_stats_%s", inst_id); create_proc_read_entry(proc_name, 0, 0, emf_stats_get, emfi); sprintf(proc_name, "net/emfdb_%s", inst_id); create_proc_read_entry(proc_name, 0, 0, emf_mfdb_list, emfi); #endif /* CONFIG_PROC_FS */ /* Add to the global EMF instance list */ emfi->next = emf->list_head; emf->list_head = emfi; return (emfi); }