12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031 |
- /*************************************************************************
- * -----------------------------------------------------------------------
- * Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- * -----------------------------------------------------------------------
- * DESCRIPTION
- * Main file for eMBMs Tunneling Module in kernel.
- *************************************************************************
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/fs.h>
- #include <net/ip.h>
- #include <linux/uaccess.h>
- #include <linux/types.h>
- #include <linux/version.h>
- #include <linux/etherdevice.h>
- #include <linux/inetdevice.h>
- #include <linux/netfilter.h>
- #include <net/arp.h>
- #include <net/neighbour.h>
- #include <linux/skbuff.h>
- #include <linux/list.h>
- #include <linux/in.h>
- #include <net/netfilter/nf_conntrack.h>
- #include <linux/miscdevice.h>
- #include "embms_kernel.h"
- struct embms_info_internal embms_conf;
- /* Global structures used for tunneling. These include
- * iphdr and udphdr which are appended to skbs for
- * tunneling, net_device and tunnleing related
- * structs and params
- */
- unsigned char hdr_buff[sizeof(struct iphdr) + sizeof(struct udphdr)];
- struct iphdr *iph_global;
- struct udphdr *udph_global;
- struct net_device *dev_global;
- static struct tmgi_to_clnt_info tmgi_to_clnt_map_tbl;
- /* handle_multicast_stream - packet forwarding
- * function for multicast stream
- * Main use case is for EMBMS Over Softap feature
- */
- static int handle_multicast_stream(struct sk_buff *skb)
- {
- struct iphdr *iph;
- struct udphdr *udph;
- unsigned char *tmp_ptr = NULL;
- struct sk_buff *skb_new = NULL;
- struct sk_buff *skb_cpy = NULL;
- struct clnt_info *temp_client = NULL;
- struct tmgi_to_clnt_info *temp_tmgi = NULL;
- struct list_head *tmgi_entry_ptr, *prev_tmgi_entry_ptr;
- struct list_head *clnt_ptr, *prev_clnt_ptr;
- int hdr_size = sizeof(*udph) + sizeof(*iph) + ETH_HLEN;
- /* only IP packets */
- if (htons(ETH_P_IP) != skb->protocol) {
- embms_error("Not an IP packet\n");
- return 0;
- }
- if (embms_conf.embms_tunneling_status == TUNNELING_OFF) {
- embms_debug("Tunneling Disabled. Can't process packets\n");
- return 0;
- }
- if (unlikely(memcmp(skb->dev->name, embms_conf.embms_iface,
- strlen(embms_conf.embms_iface)) != 0)) {
- embms_error("Packet received on %s iface. NOT an EMBMS Iface\n",
- skb->dev->name);
- return 0;
- }
- /* Check if dst ip of packet is same as multicast ip of any tmgi*/
- iph = (struct iphdr *)skb->data;
- udph = (struct udphdr *)(skb->data + sizeof(struct iphdr));
- spin_lock_bh(&embms_conf.lock);
- list_for_each_safe(tmgi_entry_ptr, prev_tmgi_entry_ptr,
- &tmgi_to_clnt_map_tbl.tmgi_list_ptr) {
- temp_tmgi = list_entry(tmgi_entry_ptr,
- struct tmgi_to_clnt_info,
- tmgi_list_ptr);
- if ((temp_tmgi->tmgi_multicast_addr == iph->daddr) &&
- (temp_tmgi->tmgi_port == udph->dest))
- break;
- }
- if (tmgi_entry_ptr == &tmgi_to_clnt_map_tbl.tmgi_list_ptr) {
- embms_error("handle_multicast_stream:");
- embms_error("could not find matchin tmgi entry\n");
- spin_unlock_bh(&embms_conf.lock);
- return 0;
- }
- /* Found a matching tmgi entry. Realloc headroom to
- * accommodate new Ethernet, IP and UDP header
- */
- skb_new = skb_realloc_headroom(skb, hdr_size);
- if (unlikely(!skb_new)) {
- embms_error("Can't allocate headroom\n");
- spin_unlock_bh(&embms_conf.lock);
- return 0;
- }
- /* push skb->data and copy IP and UDP headers*/
- tmp_ptr = skb_push(skb_new,
- sizeof(struct udphdr) + sizeof(struct iphdr));
- iph = (struct iphdr *)tmp_ptr;
- udph = (struct udphdr *)(tmp_ptr + sizeof(struct iphdr));
- memcpy(tmp_ptr, hdr_buff, hdr_size - ETH_HLEN);
- udph->len = htons(skb_new->len - sizeof(struct iphdr));
- iph->tot_len = htons(skb_new->len);
- list_for_each_safe(clnt_ptr, prev_clnt_ptr,
- &temp_tmgi->client_list_head) {
- temp_client = list_entry(clnt_ptr,
- struct clnt_info,
- client_list_ptr);
- /* Make a copy of skb_new with new IP and UDP header.
- * We can't use skb_new or its clone here since we need to
- * constantly change dst ip and dst port which is not possible
- * for shared memory as is the case with skb_new.
- */
- skb_cpy = skb_copy(skb_new, GFP_ATOMIC);
- if (unlikely(!skb_cpy)) {
- embms_error("Can't copy skb\n");
- kfree_skb(skb_new);
- return 0;
- }
- iph = (struct iphdr *)skb_cpy->data;
- udph = (struct udphdr *)(skb_cpy->data + sizeof(struct iphdr));
- iph->id = htons(atomic_inc_return(&embms_conf.ip_ident));
- /* Calculate checksum for new IP and UDP header*/
- udph->dest = temp_client->port;
- skb_cpy->csum = csum_partial((char *)udph,
- ntohs(udph->len),
- skb_cpy->csum);
- iph->daddr = temp_client->addr;
- ip_send_check(iph);
- udph->check = 0;
- udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
- ntohs(udph->len),
- IPPROTO_UDP,
- skb_cpy->csum);
- if (udph->check == 0)
- udph->check = CSUM_MANGLED_0;
- if (unlikely(!dev_global)) {
- embms_error("Global device NULL\n");
- kfree_skb(skb_cpy);
- kfree_skb(skb_new);
- return 0;
- }
- /* update device info and add MAC header*/
- skb_cpy->dev = dev_global;
- skb_cpy->dev->header_ops->create(skb_cpy, skb_cpy->dev,
- ETH_P_IP, temp_client->dmac,
- NULL, skb_cpy->len);
- dev_queue_xmit(skb_cpy);
- }
- spin_unlock_bh(&embms_conf.lock);
- kfree_skb(skb_new);
- return 1;
- }
- static int check_embms_device(atomic_t *use_count)
- {
- int ret;
- if (atomic_inc_return(use_count) == 1) {
- ret = 0;
- } else {
- atomic_dec(use_count);
- ret = -EBUSY;
- }
- return ret;
- }
- static int embms_device_open(struct inode *inode, struct file *file)
- {
- /*Check if the device is busy*/
- if (check_embms_device(&embms_conf.device_under_use)) {
- embms_error("embms_tm_open : EMBMS device busy\n");
- return -EBUSY;
- }
- try_module_get(THIS_MODULE);
- return SUCCESS;
- }
- static int embms_device_release(struct inode *inode, struct file *file)
- {
- /* Reduce device use count before leaving*/
- embms_debug("Releasing EMBMS device..\n");
- atomic_dec(&embms_conf.device_under_use);
- embms_conf.embms_tunneling_status = TUNNELING_OFF;
- module_put(THIS_MODULE);
- return SUCCESS;
- }
- static struct tmgi_to_clnt_info *check_for_tmgi_entry(u32 addr,
- u16 port)
- {
- struct list_head *tmgi_ptr, *prev_tmgi_ptr;
- struct tmgi_to_clnt_info *temp_tmgi = NULL;
- embms_debug("check_for_tmgi_entry: mcast addr :%pI4, port %u\n",
- &addr, ntohs(port));
- list_for_each_safe(tmgi_ptr,
- prev_tmgi_ptr,
- &tmgi_to_clnt_map_tbl.tmgi_list_ptr) {
- temp_tmgi = list_entry(tmgi_ptr,
- struct tmgi_to_clnt_info,
- tmgi_list_ptr);
- if ((temp_tmgi->tmgi_multicast_addr == addr) &&
- (temp_tmgi->tmgi_port == port)) {
- embms_debug("check_for_tmgi_entry:TMGI entry found\n");
- return temp_tmgi;
- }
- }
- return NULL;
- }
- static struct clnt_info *chk_clnt_entry(struct tmgi_to_clnt_info *tmgi,
- struct tmgi_to_clnt_info_update *clnt)
- {
- struct list_head *clnt_ptr, *prev_clnt_ptr;
- struct clnt_info *temp_client = NULL;
- embms_debug("check_for_client_entry: clnt addr :%pI4, port %u\n",
- &clnt->client_addr, ntohs(clnt->client_port));
- list_for_each_safe(clnt_ptr,
- prev_clnt_ptr,
- &tmgi->client_list_head) {
- temp_client = list_entry(clnt_ptr,
- struct clnt_info,
- client_list_ptr);
- if ((temp_client->addr == clnt->client_addr) &&
- (temp_client->port == clnt->client_port)) {
- embms_debug("Clnt entry present\n");
- return temp_client;
- }
- }
- return NULL;
- }
- static int add_new_tmgi_entry(struct tmgi_to_clnt_info_update *info_update,
- struct clnt_info *clnt)
- {
- struct tmgi_to_clnt_info *new_tmgi = NULL;
- embms_debug("add_new_tmgi_entry:Enter\n");
- new_tmgi = kzalloc(sizeof(*new_tmgi),
- GFP_ATOMIC);
- if (!new_tmgi) {
- embms_error("add_new_tmgi_entry: mem alloc failed\n");
- return -ENOMEM;
- }
- memset(new_tmgi, 0, sizeof(struct tmgi_to_clnt_info));
- new_tmgi->tmgi_multicast_addr = info_update->multicast_addr;
- new_tmgi->tmgi_port = info_update->multicast_port;
- embms_debug("add_new_tmgi_entry:");
- embms_debug("New tmgi multicast addr :%pI4 , port %u\n",
- &info_update->multicast_addr,
- ntohs(info_update->multicast_port));
- embms_debug("add_new_tmgi_entry:Adding client entry\n");
- spin_lock_bh(&embms_conf.lock);
- INIT_LIST_HEAD(&new_tmgi->client_list_head);
- list_add(&clnt->client_list_ptr,
- &new_tmgi->client_list_head);
- new_tmgi->no_of_clients++;
- /* Once above steps are done successfully,
- * we add tmgi entry to our local table
- */
- list_add(&new_tmgi->tmgi_list_ptr,
- &tmgi_to_clnt_map_tbl.tmgi_list_ptr);
- embms_conf.no_of_tmgi_sessions++;
- spin_unlock_bh(&embms_conf.lock);
- return SUCCESS;
- }
- static void print_tmgi_to_client_table(void)
- {
- int i, j;
- struct clnt_info *temp_client = NULL;
- struct tmgi_to_clnt_info *temp_tmgi = NULL;
- struct list_head *tmgi_entry_ptr, *prev_tmgi_entry_ptr;
- struct list_head *clnt_ptr, *prev_clnt_ptr;
- embms_debug("====================================================\n");
- embms_debug("Printing TMGI to Client Table :\n");
- embms_debug("No of Active TMGIs : %d\n",
- embms_conf.no_of_tmgi_sessions);
- embms_debug("====================================================\n\n");
- if (embms_conf.no_of_tmgi_sessions > 0) {
- i = 1;
- list_for_each_safe(tmgi_entry_ptr, prev_tmgi_entry_ptr,
- &tmgi_to_clnt_map_tbl.tmgi_list_ptr) {
- temp_tmgi = list_entry(tmgi_entry_ptr,
- struct tmgi_to_clnt_info,
- tmgi_list_ptr);
- embms_debug("TMGI entry %d :\n", i);
- embms_debug("TMGI multicast addr : %pI4 , port %u\n\n",
- &temp_tmgi->tmgi_multicast_addr,
- ntohs(temp_tmgi->tmgi_port));
- embms_debug("No of clients : %d\n",
- temp_tmgi->no_of_clients);
- j = 1;
- list_for_each_safe(clnt_ptr, prev_clnt_ptr,
- &temp_tmgi->client_list_head) {
- temp_client = list_entry(clnt_ptr,
- struct clnt_info,
- client_list_ptr);
- embms_debug("Client entry %d :\n", j);
- embms_debug("client addr : %pI4 , port %u\n\n",
- &temp_client->addr,
- ntohs(temp_client->port));
- j++;
- }
- i++;
- embms_debug("===========================================\n\n");
- }
- } else {
- embms_debug("No TMGI entries to Display\n");
- }
- embms_debug("==================================================================\n\n");
- }
- /**
- * delete_tmgi_entry_from_table() - deletes tmgi from global tmgi-client table
- * @buffer: Buffer containing TMGI info for deletion.
- *
- * This function completely removes the TMGI from
- * global TMGI-client table, along with the client list
- * so that no packets for this TMGI are processed
- *
- * Return: Success on deleting TMGI entry, error otherwise.
- */
- int delete_tmgi_entry_from_table(char *buffer)
- {
- struct tmgi_to_clnt_info_update *info_update;
- struct clnt_info *temp_client = NULL;
- struct tmgi_to_clnt_info *temp_tmgi = NULL;
- struct list_head *clnt_ptr, *prev_clnt_ptr;
- embms_debug("delete_tmgi_entry_from_table: Enter\n");
- info_update = (struct tmgi_to_clnt_info_update *)buffer;
- if (!info_update) {
- embms_error("delete_tmgi_entry_from_table:");
- embms_error("NULL arguments passed\n");
- return -EBADPARAM;
- }
- /* This function is used to delete a specific TMGI entry
- * when that particular TMGI goes down
- * Search for the TMGI entry in our local table
- */
- if (embms_conf.no_of_tmgi_sessions == 0) {
- embms_error("TMGI count 0. Nothing to delete\n");
- return SUCCESS;
- }
- temp_tmgi = check_for_tmgi_entry(info_update->multicast_addr,
- info_update->multicast_port);
- if (!temp_tmgi) {
- /* TMGI entry was not found in our local table*/
- embms_error("delete_client_entry_from_table :");
- embms_error("Desired TMGI entry not found\n");
- return -EBADPARAM;
- }
- spin_lock_bh(&embms_conf.lock);
- /* We need to free memory allocated to client entries
- * for a particular TMGI entry
- */
- list_for_each_safe(clnt_ptr, prev_clnt_ptr,
- &temp_tmgi->client_list_head) {
- temp_client = list_entry(clnt_ptr,
- struct clnt_info,
- client_list_ptr);
- embms_debug("delete_tmgi_entry_from_table :");
- embms_debug("Client addr to delete :%pI4 , port %u\n",
- &temp_client->addr, ntohs(temp_client->port));
- list_del(&temp_client->client_list_ptr);
- temp_tmgi->no_of_clients--;
- kfree(temp_client);
- }
- /* Free memory allocated to tmgi entry*/
- list_del(&temp_tmgi->tmgi_list_ptr);
- kfree(temp_tmgi);
- embms_conf.no_of_tmgi_sessions--;
- spin_unlock_bh(&embms_conf.lock);
- embms_debug("delete_tmgi_entry_from_table : TMGI Entry deleted.\n");
- return SUCCESS;
- }
- /**
- * delete_client_entry_from_all_tmgi() - deletes client from all tmgi lists
- * @buffer: Buffer containing client info for deletion.
- *
- * This function completely removes a client from
- * all TMGIs in global TMGI-client table. Also delets TMGI
- * entries if no more clients are there
- *
- * Return: Success on deleting client entry, error otherwise.
- */
- int delete_client_entry_from_all_tmgi(char *buffer)
- {
- struct tmgi_to_clnt_info_update *info_update;
- struct clnt_info *temp_client = NULL;
- struct tmgi_to_clnt_info *tmgi = NULL;
- struct list_head *tmgi_entry_ptr, *prev_tmgi_entry_ptr;
- /* We use this function when we want to delete any
- * client entry from all TMGI entries. This scenario
- * happens when any client disconnects and hence
- * we need to clean all realted client entries
- * in our mapping table
- */
- embms_debug("del_clnt_from_all_tmgi: Enter\n");
- info_update = (struct tmgi_to_clnt_info_update *)buffer;
- if (!info_update) {
- embms_error("del_clnt_from_all_tmgi:");
- embms_error("NULL arguments passed\n");
- return -EBADPARAM;
- }
- /* We start checking from first TMGI entry and if client
- * entry is found in client entries of any TMGI, we clean
- * up that client entry from that TMGI entry
- */
- if (embms_conf.no_of_tmgi_sessions == 0)
- return SUCCESS;
- list_for_each_safe(tmgi_entry_ptr, prev_tmgi_entry_ptr,
- &tmgi_to_clnt_map_tbl.tmgi_list_ptr) {
- tmgi = list_entry(tmgi_entry_ptr,
- struct tmgi_to_clnt_info,
- tmgi_list_ptr);
- temp_client = chk_clnt_entry(tmgi, info_update);
- if (!temp_client)
- continue;
- spin_lock_bh(&embms_conf.lock);
- list_del(&temp_client->client_list_ptr);
- tmgi->no_of_clients--;
- kfree(temp_client);
- spin_unlock_bh(&embms_conf.lock);
- temp_client = NULL;
- if (tmgi->no_of_clients == 0) {
- /* Deleted clnt was the only clnt for
- * that TMGI we need to delete TMGI
- * entry from table
- */
- embms_debug("del_clnt_from_all_tmgi:");
- embms_debug("Deleted client was ");
- embms_debug("last client for tmgi\n");
- embms_debug("del_clnt_from_all_tmgi:");
- embms_debug("Delting tmgi as it has ");
- embms_debug("zero clients.TMGI IP ");
- embms_debug(":%pI4 , port %u\n",
- &tmgi->tmgi_multicast_addr,
- ntohs(tmgi->tmgi_port));
- spin_lock_bh(&embms_conf.lock);
- list_del(&tmgi->tmgi_list_ptr);
- embms_conf.no_of_tmgi_sessions--;
- kfree(tmgi);
- spin_unlock_bh(&embms_conf.lock);
- embms_debug("del_clnt_from_all_tmgi:");
- embms_debug("TMGI entry deleted\n");
- }
- }
- embms_debug("del_clnt_from_all_tmgi Successful\n");
- return SUCCESS;
- }
- /**
- * add_client_entry_to_table() - add client entry to specified TMGI
- * @buffer: Buffer containing client info for addition.
- *
- * This function adds a client to the specified TMGI in
- * the global TMGI-client table. If TMGI entry is not
- * present, it adds a new TMGI entry and adds client
- * entry to it.
- *
- * Return: Success on adding client entry, error otherwise.
- */
- int add_client_entry_to_table(char *buffer)
- {
- int ret;
- struct tmgi_to_clnt_info_update *info_update;
- struct clnt_info *new_client = NULL;
- struct tmgi_to_clnt_info *tmgi = NULL;
- struct neighbour *neigh_entry;
- embms_debug("add_client_entry_to_table: Enter\n");
- info_update = (struct tmgi_to_clnt_info_update *)buffer;
- if (!info_update) {
- embms_error("add_client_entry_to_table:");
- embms_error("NULL arguments passed\n");
- return -EBADPARAM;
- }
- new_client = kzalloc(sizeof(*new_client), GFP_ATOMIC);
- if (!new_client) {
- embms_error("add_client_entry_to_table:");
- embms_error("Cannot allocate memory\n");
- return -ENOMEM;
- }
- new_client->addr = info_update->client_addr;
- new_client->port = info_update->client_port;
- neigh_entry = __ipv4_neigh_lookup(dev_global,
- (u32)(new_client->addr));
- if (!neigh_entry) {
- embms_error("add_client_entry_to_table :");
- embms_error("Can't find neighbour entry\n");
- kfree(new_client);
- return -EBADPARAM;
- }
- ether_addr_copy(new_client->dmac, neigh_entry->ha);
- embms_debug("DMAC of client : %pM\n", new_client->dmac);
- embms_debug("add_client_entry_to_table:");
- embms_debug("New client addr :%pI4 , port %u\n",
- &info_update->client_addr,
- ntohs(info_update->client_port));
- if (embms_conf.no_of_tmgi_sessions == 0) {
- /* TMGI Client mapping table is empty.
- * First client entry is being added
- */
- embms_debug("tmgi_to_clnt_map_tbl is empty\n");
- ret = add_new_tmgi_entry(info_update, new_client);
- if (ret != SUCCESS) {
- kfree(new_client);
- new_client = NULL;
- }
- goto exit_add;
- }
- /* In this case, table already has some entries
- * and we need to search for the specific tmgi entry
- * for which client entry is to be added
- */
- tmgi = check_for_tmgi_entry(info_update->multicast_addr,
- info_update->multicast_port);
- if (tmgi) {
- if (chk_clnt_entry(tmgi, info_update)) {
- kfree(new_client);
- return -ENOEFFECT;
- }
- /* Adding client to the client list
- * for the specified TMGI
- */
- spin_lock_bh(&embms_conf.lock);
- list_add(&new_client->client_list_ptr,
- &tmgi->client_list_head);
- tmgi->no_of_clients++;
- spin_unlock_bh(&embms_conf.lock);
- ret = SUCCESS;
- } else {
- /* TMGI specified in the message was not found in
- * mapping table.Hence, we need to add a new entry
- * for this TMGI and add the specified client to the client
- * list
- */
- embms_debug("TMGI entry not present. Adding tmgi entry\n");
- ret = add_new_tmgi_entry(info_update, new_client);
- if (ret != SUCCESS) {
- kfree(new_client);
- new_client = NULL;
- }
- }
- exit_add:
- return ret;
- }
- /**
- * delete_client_entry_from_table() - delete client entry from specified TMGI
- * @buffer: Buffer containing client info for deletion.
- *
- * This function deletes a client from the specified TMGI in
- * the global TMGI-client table. If this was the last client
- * entry, it also deletes the TMGI entry.
- *
- * Return: Success on deleting client entry, error otherwise.
- */
- int delete_client_entry_from_table(char *buffer)
- {
- struct tmgi_to_clnt_info_update *info_update;
- struct clnt_info *temp_client = NULL;
- struct tmgi_to_clnt_info *temp_tmgi = NULL;
- embms_debug("delete_client_entry_from_table: Enter\n");
- info_update = (struct tmgi_to_clnt_info_update *)buffer;
- if (!info_update) {
- embms_error("delete_client_entry_from_table:");
- embms_error("NULL arguments passed\n");
- return -EBADPARAM;
- }
- /* Search for the TMGI entry*/
- if (embms_conf.no_of_tmgi_sessions == 0)
- return SUCCESS;
- temp_tmgi = check_for_tmgi_entry(info_update->multicast_addr,
- info_update->multicast_port);
- if (!temp_tmgi) {
- embms_error("delete_client_entry_from_table:TMGI not found\n");
- return -EBADPARAM;
- }
- /* Delete client entry for a specific tmgi*/
- embms_debug("delete_client_entry_from_table:clnt addr :%pI4,port %u\n",
- &info_update->client_addr,
- ntohs(info_update->client_port));
- temp_client = chk_clnt_entry(temp_tmgi, info_update);
- if (!temp_client) {
- /* Specified client entry was not found in client list
- * of specified TMGI
- */
- embms_error("delete_client_entry_from_table:Clnt not found\n");
- return -EBADPARAM;
- }
- spin_lock_bh(&embms_conf.lock);
- list_del(&temp_client->client_list_ptr);
- temp_tmgi->no_of_clients--;
- spin_unlock_bh(&embms_conf.lock);
- kfree(temp_client);
- temp_client = NULL;
- embms_debug("delete_client_entry_from_table:Client entry deleted\n");
- if (temp_tmgi->no_of_clients == 0) {
- /* If deleted client was the only client for that TMGI
- * we need to delete TMGI entry from table
- */
- embms_debug("delete_client_entry_from_table:");
- embms_debug("Deleted client was the last client for tmgi\n");
- embms_debug("delete_client_entry_from_table:");
- embms_debug("Deleting tmgi since it has zero clients\n");
- spin_lock_bh(&embms_conf.lock);
- list_del(&temp_tmgi->tmgi_list_ptr);
- embms_conf.no_of_tmgi_sessions--;
- kfree(temp_tmgi);
- spin_unlock_bh(&embms_conf.lock);
- embms_debug("delete_client_entry_from_table: TMGI deleted\n");
- }
- if (embms_conf.no_of_tmgi_sessions == 0)
- embms_conf.embms_tunneling_status = TUNNELING_OFF;
- return SUCCESS;
- }
- /**
- * embms_device_ioctl() - handle IOCTL calls to device
- * @file: File descriptor of file opened from userspace process
- * @ioctl_num: IOCTL to use
- * @ioctl_param: IOCTL parameters/arguments
- *
- * This function is called whenever a process tries to do
- * an ioctl on our device file. As per the IOCTL number,
- * it calls various functions to manipulate global
- * TMGI-client table
- *
- * Return: Success if functoin call returns SUCCESS, error otherwise.
- */
- long embms_device_ioctl(struct file *file, unsigned int ioctl_num,
- unsigned long ioctl_param)
- {
- int ret;
- char buffer[BUF_LEN];
- struct in_device *iface_dev;
- struct in_ifaddr *iface_info;
- struct tmgi_to_clnt_info_update *info_update;
- char __user *argp = (char __user *)ioctl_param;
- memset(buffer, 0, BUF_LEN);
- /* Switch according to the ioctl called*/
- switch (ioctl_num) {
- case ADD_EMBMS_TUNNEL:
- if (copy_from_user(buffer, argp,
- sizeof(struct tmgi_to_clnt_info_update)))
- return -EFAULT;
- ret = add_client_entry_to_table(buffer);
- print_tmgi_to_client_table();
- break;
- case DEL_EMBMS_TUNNEL:
- if (copy_from_user(buffer, argp,
- sizeof(struct tmgi_to_clnt_info_update)))
- return -EFAULT;
- ret = delete_client_entry_from_table(buffer);
- print_tmgi_to_client_table();
- break;
- case TMGI_DEACTIVATE:
- if (copy_from_user(buffer, argp,
- sizeof(struct tmgi_to_clnt_info_update)))
- return -EFAULT;
- ret = delete_tmgi_entry_from_table(buffer);
- print_tmgi_to_client_table();
- break;
- case CLIENT_DEACTIVATE:
- if (copy_from_user(buffer, argp,
- sizeof(struct tmgi_to_clnt_info_update)))
- return -EFAULT;
- ret = delete_client_entry_from_all_tmgi(buffer);
- print_tmgi_to_client_table();
- break;
- case GET_EMBMS_TUNNELING_STATUS:
- /* This ioctl is both input (ioctl_param) and
- * output (the return value of this function)
- */
- embms_debug("Sending tunneling status : %d\n",
- embms_conf.embms_tunneling_status);
- ret = embms_conf.embms_tunneling_status;
- break;
- case START_EMBMS_TUNNEL:
- if (copy_from_user(buffer, argp,
- sizeof(struct tmgi_to_clnt_info_update)))
- return -EFAULT;
- info_update = (struct tmgi_to_clnt_info_update *)buffer;
- embms_conf.embms_data_port = info_update->data_port;
- udph_global->source = embms_conf.embms_data_port;
- memset(embms_conf.embms_iface, 0, EMBMS_MAX_IFACE_NAME);
- memcpy(embms_conf.embms_iface, info_update->iface_name,
- EMBMS_MAX_IFACE_NAME);
- embms_conf.embms_tunneling_status = TUNNELING_ON;
- embms_debug("Starting Tunneling. Embms_data_port = %d\n",
- ntohs(embms_conf.embms_data_port));
- embms_debug("Embms Data Iface = %s\n", embms_conf.embms_iface);
- ret = SUCCESS;
- /*Initialise dev_global to bridge device*/
- dev_global = __dev_get_by_name(&init_net, BRIDGE_IFACE);
- if (!dev_global) {
- embms_error("Error in getting device info\n");
- ret = FAILURE;
- } else {
- iface_dev = (struct in_device *)dev_global->ip_ptr;
- iface_info = iface_dev->ifa_list;
- while (iface_info) {
- if (memcmp(iface_info->ifa_label,
- BRIDGE_IFACE,
- strlen(BRIDGE_IFACE)) == 0)
- break;
- iface_info = iface_info->ifa_next;
- }
- if (iface_info) {
- embms_debug("IP address of %s iface is %pI4\n",
- BRIDGE_IFACE,
- &iface_info->ifa_address);
- /*Populate source addr for header*/
- iph_global->saddr = iface_info->ifa_address;
- ret = SUCCESS;
- } else {
- embms_debug("Could not find iface address\n");
- ret = FAILURE;
- }
- }
- break;
- case STOP_EMBMS_TUNNEL:
- embms_conf.embms_tunneling_status = TUNNELING_OFF;
- embms_debug("Stopped Tunneling..\n");
- ret = SUCCESS;
- break;
- }
- return ret;
- }
- /* Module Declarations
- * This structure will hold the functions to be called
- * when a process does something to the device we
- * created. Since a pointer to this structure is kept in
- * the devices table, it can't be local to
- * init_module. NULL is for unimplemented functions.
- */
- static const struct file_operations embms_device_fops = {
- .owner = THIS_MODULE,
- .open = embms_device_open,
- .release = embms_device_release,
- .read = NULL,
- .write = NULL,
- .unlocked_ioctl = embms_device_ioctl,
- };
- static int embms_ioctl_init(void)
- {
- int ret;
- struct device *dev;
- ret = alloc_chrdev_region(&device, 0, dev_num, EMBMS_DEVICE_NAME);
- if (ret) {
- embms_error("device_alloc err\n");
- goto dev_alloc_err;
- }
- embms_class = class_create(THIS_MODULE, EMBMS_DEVICE_NAME);
- if (IS_ERR(embms_class)) {
- embms_error("class_create err\n");
- goto class_err;
- }
- dev = device_create(embms_class, NULL, device,
- &embms_conf, EMBMS_DEVICE_NAME);
- if (IS_ERR(dev)) {
- embms_error("device_create err\n");
- goto device_err;
- }
- cdev_init(&embms_device, &embms_device_fops);
- ret = cdev_add(&embms_device, device, dev_num);
- if (ret) {
- embms_error("cdev_add err\n");
- goto cdev_add_err;
- }
- embms_debug("ioctl init OK!!\n");
- return 0;
- cdev_add_err:
- device_destroy(embms_class, device);
- device_err:
- class_destroy(embms_class);
- class_err:
- unregister_chrdev_region(device, dev_num);
- dev_alloc_err:
- return -ENODEV;
- }
- static void embms_ioctl_deinit(void)
- {
- cdev_del(&embms_device);
- device_destroy(embms_class, device);
- class_destroy(embms_class);
- unregister_chrdev_region(device, dev_num);
- }
- /*Initialize the module - Register the misc device*/
- static int __init start_embms(void)
- {
- int ret = 0;
- iph_global = (struct iphdr *)hdr_buff;
- udph_global = (struct udphdr *)(hdr_buff + sizeof(struct iphdr));
- embms_conf.embms_tunneling_status = TUNNELING_OFF;
- embms_conf.no_of_tmgi_sessions = 0;
- embms_conf.embms_data_port = 0;
- atomic_set(&embms_conf.device_under_use, 0);
- atomic_set(&embms_conf.ip_ident, 0);
- spin_lock_init(&embms_conf.lock);
- embms_debug("Registering embms device\n");
- ret = embms_ioctl_init();
- if (ret) {
- embms_error("embms device failed to register");
- goto fail_init;
- }
- INIT_LIST_HEAD(&tmgi_to_clnt_map_tbl.tmgi_list_ptr);
- memset(hdr_buff, 0, sizeof(struct udphdr) + sizeof(struct iphdr));
- udph_global->check = UDP_CHECKSUM;
- iph_global->version = IP_VERSION;
- iph_global->ihl = IP_IHL;
- iph_global->tos = IP_TOS;
- iph_global->frag_off = IP_FRAG_OFFSET;
- iph_global->ttl = IP_TTL;
- iph_global->protocol = IPPROTO_UDP;
- dev_global = NULL;
- if (!embms_tm_multicast_recv)
- RCU_INIT_POINTER(embms_tm_multicast_recv,
- handle_multicast_stream);
- return ret;
- fail_init:
- embms_ioctl_deinit();
- return ret;
- }
- /*Cleanup - unregister the appropriate file from proc*/
- static void __exit stop_embms(void)
- {
- embms_ioctl_deinit();
- if (rcu_dereference(embms_tm_multicast_recv))
- RCU_INIT_POINTER(embms_tm_multicast_recv, NULL);
- embms_debug("unregister_chrdev done\n");
- }
- module_init(start_embms);
- module_exit(stop_embms);
- MODULE_LICENSE("GPL v2");
|