Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 1 | /************************************************************************* |
| 2 | * ----------------------------------------------------------------------- |
| 3 | * Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. |
| 4 | |
| 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License version 2 and |
| 7 | * only version 2 as published by the Free Software Foundation. |
| 8 | |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| 12 | * See the GNU General Public License for more details. |
| 13 | * ----------------------------------------------------------------------- |
| 14 | |
| 15 | * DESCRIPTION |
| 16 | * Main file for eMBMs Tunneling Module in kernel. |
| 17 | ************************************************************************* |
| 18 | */ |
| 19 | |
| 20 | #include <linux/kernel.h> |
| 21 | #include <linux/module.h> |
| 22 | #include <linux/fs.h> |
| 23 | #include <net/ip.h> |
| 24 | #include <linux/uaccess.h> |
| 25 | #include <linux/types.h> |
| 26 | #include <linux/version.h> |
| 27 | #include <linux/etherdevice.h> |
| 28 | |
| 29 | #include <linux/inetdevice.h> |
| 30 | #include <linux/netfilter.h> |
| 31 | #include <net/arp.h> |
| 32 | #include <net/neighbour.h> |
| 33 | |
| 34 | #include <linux/skbuff.h> |
| 35 | #include <linux/list.h> |
| 36 | #include <linux/in.h> |
| 37 | #include <net/netfilter/nf_conntrack.h> |
| 38 | #include <linux/miscdevice.h> |
| 39 | #include "embms_kernel.h" |
| 40 | |
| 41 | struct embms_info_internal embms_conf; |
| 42 | |
| 43 | /* Global structures used for tunneling. These include |
| 44 | * iphdr and udphdr which are appended to skbs for |
| 45 | * tunneling, net_device and tunnleing related |
| 46 | * structs and params |
| 47 | */ |
| 48 | |
| 49 | unsigned char hdr_buff[sizeof(struct iphdr) + sizeof(struct udphdr)]; |
| 50 | struct iphdr *iph_global; |
| 51 | struct udphdr *udph_global; |
| 52 | struct net_device *dev_global; |
| 53 | |
| 54 | static struct tmgi_to_clnt_info tmgi_to_clnt_map_tbl; |
| 55 | |
| 56 | /* handle_multicast_stream - packet forwarding |
| 57 | * function for multicast stream |
| 58 | * Main use case is for EMBMS Over Softap feature |
| 59 | */ |
| 60 | |
| 61 | static int handle_multicast_stream(struct sk_buff *skb) |
| 62 | { |
| 63 | struct iphdr *iph; |
| 64 | struct udphdr *udph; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 65 | unsigned char *tmp_ptr = NULL; |
| 66 | struct sk_buff *skb_new = NULL; |
| 67 | struct sk_buff *skb_cpy = NULL; |
| 68 | struct clnt_info *temp_client = NULL; |
| 69 | struct tmgi_to_clnt_info *temp_tmgi = NULL; |
| 70 | struct list_head *tmgi_entry_ptr, *prev_tmgi_entry_ptr; |
| 71 | struct list_head *clnt_ptr, *prev_clnt_ptr; |
| 72 | int hdr_size = sizeof(*udph) + sizeof(*iph) + ETH_HLEN; |
| 73 | |
| 74 | /* only IP packets */ |
| 75 | if (htons(ETH_P_IP) != skb->protocol) { |
| 76 | embms_error("Not an IP packet\n"); |
| 77 | return 0; |
| 78 | } |
| 79 | |
| 80 | if (embms_conf.embms_tunneling_status == TUNNELING_OFF) { |
| 81 | embms_debug("Tunneling Disabled. Can't process packets\n"); |
| 82 | return 0; |
| 83 | } |
| 84 | |
| 85 | if (unlikely(memcmp(skb->dev->name, embms_conf.embms_iface, |
| 86 | strlen(embms_conf.embms_iface)) != 0)) { |
| 87 | embms_error("Packet received on %s iface. NOT an EMBMS Iface\n", |
| 88 | skb->dev->name); |
| 89 | return 0; |
| 90 | } |
| 91 | |
| 92 | /* Check if dst ip of packet is same as multicast ip of any tmgi*/ |
| 93 | |
| 94 | iph = (struct iphdr *)skb->data; |
| 95 | udph = (struct udphdr *)(skb->data + sizeof(struct iphdr)); |
| 96 | |
| 97 | spin_lock_bh(&embms_conf.lock); |
| 98 | |
| 99 | list_for_each_safe(tmgi_entry_ptr, prev_tmgi_entry_ptr, |
| 100 | &tmgi_to_clnt_map_tbl.tmgi_list_ptr) { |
| 101 | temp_tmgi = list_entry(tmgi_entry_ptr, |
| 102 | struct tmgi_to_clnt_info, |
| 103 | tmgi_list_ptr); |
| 104 | |
| 105 | if ((temp_tmgi->tmgi_multicast_addr == iph->daddr) && |
| 106 | (temp_tmgi->tmgi_port == udph->dest)) |
| 107 | break; |
| 108 | } |
| 109 | |
| 110 | if (tmgi_entry_ptr == &tmgi_to_clnt_map_tbl.tmgi_list_ptr) { |
| 111 | embms_error("handle_multicast_stream:"); |
| 112 | embms_error("could not find matchin tmgi entry\n"); |
| 113 | spin_unlock_bh(&embms_conf.lock); |
| 114 | return 0; |
| 115 | } |
| 116 | |
| 117 | /* Found a matching tmgi entry. Realloc headroom to |
| 118 | * accommodate new Ethernet, IP and UDP header |
| 119 | */ |
| 120 | |
| 121 | skb_new = skb_realloc_headroom(skb, hdr_size); |
| 122 | if (unlikely(!skb_new)) { |
| 123 | embms_error("Can't allocate headroom\n"); |
| 124 | spin_unlock_bh(&embms_conf.lock); |
| 125 | return 0; |
| 126 | } |
| 127 | |
| 128 | /* push skb->data and copy IP and UDP headers*/ |
| 129 | |
| 130 | tmp_ptr = skb_push(skb_new, |
| 131 | sizeof(struct udphdr) + sizeof(struct iphdr)); |
| 132 | |
| 133 | iph = (struct iphdr *)tmp_ptr; |
| 134 | udph = (struct udphdr *)(tmp_ptr + sizeof(struct iphdr)); |
| 135 | |
| 136 | memcpy(tmp_ptr, hdr_buff, hdr_size - ETH_HLEN); |
| 137 | udph->len = htons(skb_new->len - sizeof(struct iphdr)); |
| 138 | iph->tot_len = htons(skb_new->len); |
| 139 | |
| 140 | list_for_each_safe(clnt_ptr, prev_clnt_ptr, |
| 141 | &temp_tmgi->client_list_head) { |
| 142 | temp_client = list_entry(clnt_ptr, |
| 143 | struct clnt_info, |
| 144 | client_list_ptr); |
| 145 | |
| 146 | /* Make a copy of skb_new with new IP and UDP header. |
| 147 | * We can't use skb_new or its clone here since we need to |
| 148 | * constantly change dst ip and dst port which is not possible |
| 149 | * for shared memory as is the case with skb_new. |
| 150 | */ |
| 151 | |
| 152 | skb_cpy = skb_copy(skb_new, GFP_ATOMIC); |
| 153 | if (unlikely(!skb_cpy)) { |
| 154 | embms_error("Can't copy skb\n"); |
| 155 | kfree_skb(skb_new); |
| 156 | return 0; |
| 157 | } |
| 158 | |
| 159 | iph = (struct iphdr *)skb_cpy->data; |
| 160 | udph = (struct udphdr *)(skb_cpy->data + sizeof(struct iphdr)); |
| 161 | |
| 162 | iph->id = htons(atomic_inc_return(&embms_conf.ip_ident)); |
| 163 | |
| 164 | /* Calculate checksum for new IP and UDP header*/ |
| 165 | |
| 166 | udph->dest = temp_client->port; |
| 167 | skb_cpy->csum = csum_partial((char *)udph, |
| 168 | ntohs(udph->len), |
| 169 | skb_cpy->csum); |
| 170 | |
| 171 | iph->daddr = temp_client->addr; |
| 172 | ip_send_check(iph); |
| 173 | |
| 174 | udph->check = 0; |
| 175 | udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, |
| 176 | ntohs(udph->len), |
| 177 | IPPROTO_UDP, |
| 178 | skb_cpy->csum); |
| 179 | |
| 180 | if (udph->check == 0) |
| 181 | udph->check = CSUM_MANGLED_0; |
| 182 | |
| 183 | if (unlikely(!dev_global)) { |
| 184 | embms_error("Global device NULL\n"); |
| 185 | kfree_skb(skb_cpy); |
| 186 | kfree_skb(skb_new); |
| 187 | return 0; |
| 188 | } |
| 189 | |
| 190 | /* update device info and add MAC header*/ |
| 191 | |
| 192 | skb_cpy->dev = dev_global; |
| 193 | |
| 194 | skb_cpy->dev->header_ops->create(skb_cpy, skb_cpy->dev, |
| 195 | ETH_P_IP, temp_client->dmac, |
| 196 | NULL, skb_cpy->len); |
| 197 | dev_queue_xmit(skb_cpy); |
| 198 | } |
| 199 | |
| 200 | spin_unlock_bh(&embms_conf.lock); |
| 201 | kfree_skb(skb_new); |
| 202 | return 1; |
| 203 | } |
| 204 | |
| 205 | static int check_embms_device(atomic_t *use_count) |
| 206 | { |
| 207 | int ret; |
| 208 | |
| 209 | if (atomic_inc_return(use_count) == 1) { |
| 210 | ret = 0; |
| 211 | } else { |
| 212 | atomic_dec(use_count); |
| 213 | ret = -EBUSY; |
| 214 | } |
| 215 | return ret; |
| 216 | } |
| 217 | |
| 218 | static int embms_device_open(struct inode *inode, struct file *file) |
| 219 | { |
| 220 | /*Check if the device is busy*/ |
| 221 | if (check_embms_device(&embms_conf.device_under_use)) { |
| 222 | embms_error("embms_tm_open : EMBMS device busy\n"); |
| 223 | return -EBUSY; |
| 224 | } |
| 225 | |
| 226 | try_module_get(THIS_MODULE); |
| 227 | return SUCCESS; |
| 228 | } |
| 229 | |
| 230 | static int embms_device_release(struct inode *inode, struct file *file) |
| 231 | { |
| 232 | /* Reduce device use count before leaving*/ |
| 233 | embms_debug("Releasing EMBMS device..\n"); |
| 234 | atomic_dec(&embms_conf.device_under_use); |
| 235 | embms_conf.embms_tunneling_status = TUNNELING_OFF; |
| 236 | module_put(THIS_MODULE); |
| 237 | return SUCCESS; |
| 238 | } |
| 239 | |
| 240 | static struct tmgi_to_clnt_info *check_for_tmgi_entry(u32 addr, |
| 241 | u16 port) |
| 242 | { |
| 243 | struct list_head *tmgi_ptr, *prev_tmgi_ptr; |
| 244 | struct tmgi_to_clnt_info *temp_tmgi = NULL; |
| 245 | |
| 246 | embms_debug("check_for_tmgi_entry: mcast addr :%pI4, port %u\n", |
| 247 | &addr, ntohs(port)); |
| 248 | |
| 249 | list_for_each_safe(tmgi_ptr, |
| 250 | prev_tmgi_ptr, |
| 251 | &tmgi_to_clnt_map_tbl.tmgi_list_ptr) { |
| 252 | temp_tmgi = list_entry(tmgi_ptr, |
| 253 | struct tmgi_to_clnt_info, |
| 254 | tmgi_list_ptr); |
| 255 | |
| 256 | if ((temp_tmgi->tmgi_multicast_addr == addr) && |
| 257 | (temp_tmgi->tmgi_port == port)) { |
| 258 | embms_debug("check_for_tmgi_entry:TMGI entry found\n"); |
| 259 | return temp_tmgi; |
| 260 | } |
| 261 | } |
| 262 | return NULL; |
| 263 | } |
| 264 | |
| 265 | static struct clnt_info *chk_clnt_entry(struct tmgi_to_clnt_info *tmgi, |
| 266 | struct tmgi_to_clnt_info_update *clnt) |
| 267 | { |
| 268 | struct list_head *clnt_ptr, *prev_clnt_ptr; |
| 269 | struct clnt_info *temp_client = NULL; |
| 270 | |
| 271 | embms_debug("check_for_client_entry: clnt addr :%pI4, port %u\n", |
| 272 | &clnt->client_addr, ntohs(clnt->client_port)); |
| 273 | |
| 274 | list_for_each_safe(clnt_ptr, |
| 275 | prev_clnt_ptr, |
| 276 | &tmgi->client_list_head) { |
| 277 | temp_client = list_entry(clnt_ptr, |
| 278 | struct clnt_info, |
| 279 | client_list_ptr); |
| 280 | if ((temp_client->addr == clnt->client_addr) && |
| 281 | (temp_client->port == clnt->client_port)) { |
| 282 | embms_debug("Clnt entry present\n"); |
| 283 | return temp_client; |
| 284 | } |
| 285 | } |
| 286 | return NULL; |
| 287 | } |
| 288 | |
| 289 | static int add_new_tmgi_entry(struct tmgi_to_clnt_info_update *info_update, |
| 290 | struct clnt_info *clnt) |
| 291 | { |
| 292 | struct tmgi_to_clnt_info *new_tmgi = NULL; |
| 293 | |
| 294 | embms_debug("add_new_tmgi_entry:Enter\n"); |
| 295 | |
| 296 | new_tmgi = kzalloc(sizeof(*new_tmgi), |
| 297 | GFP_ATOMIC); |
| 298 | if (!new_tmgi) { |
| 299 | embms_error("add_new_tmgi_entry: mem alloc failed\n"); |
| 300 | return -ENOMEM; |
| 301 | } |
| 302 | |
| 303 | memset(new_tmgi, 0, sizeof(struct tmgi_to_clnt_info)); |
| 304 | |
| 305 | new_tmgi->tmgi_multicast_addr = info_update->multicast_addr; |
| 306 | new_tmgi->tmgi_port = info_update->multicast_port; |
| 307 | |
| 308 | embms_debug("add_new_tmgi_entry:"); |
| 309 | embms_debug("New tmgi multicast addr :%pI4 , port %u\n", |
| 310 | &info_update->multicast_addr, |
| 311 | ntohs(info_update->multicast_port)); |
| 312 | |
| 313 | embms_debug("add_new_tmgi_entry:Adding client entry\n"); |
| 314 | |
| 315 | spin_lock_bh(&embms_conf.lock); |
| 316 | |
| 317 | INIT_LIST_HEAD(&new_tmgi->client_list_head); |
| 318 | list_add(&clnt->client_list_ptr, |
| 319 | &new_tmgi->client_list_head); |
| 320 | new_tmgi->no_of_clients++; |
| 321 | |
| 322 | /* Once above steps are done successfully, |
| 323 | * we add tmgi entry to our local table |
| 324 | */ |
| 325 | |
| 326 | list_add(&new_tmgi->tmgi_list_ptr, |
| 327 | &tmgi_to_clnt_map_tbl.tmgi_list_ptr); |
| 328 | embms_conf.no_of_tmgi_sessions++; |
| 329 | |
| 330 | spin_unlock_bh(&embms_conf.lock); |
| 331 | |
| 332 | return SUCCESS; |
| 333 | } |
| 334 | |
| 335 | static void print_tmgi_to_client_table(void) |
| 336 | { |
| 337 | int i, j; |
| 338 | struct clnt_info *temp_client = NULL; |
| 339 | struct tmgi_to_clnt_info *temp_tmgi = NULL; |
| 340 | struct list_head *tmgi_entry_ptr, *prev_tmgi_entry_ptr; |
| 341 | struct list_head *clnt_ptr, *prev_clnt_ptr; |
| 342 | |
| 343 | embms_debug("====================================================\n"); |
| 344 | embms_debug("Printing TMGI to Client Table :\n"); |
| 345 | embms_debug("No of Active TMGIs : %d\n", |
| 346 | embms_conf.no_of_tmgi_sessions); |
| 347 | embms_debug("====================================================\n\n"); |
| 348 | |
| 349 | if (embms_conf.no_of_tmgi_sessions > 0) { |
| 350 | i = 1; |
| 351 | list_for_each_safe(tmgi_entry_ptr, prev_tmgi_entry_ptr, |
| 352 | &tmgi_to_clnt_map_tbl.tmgi_list_ptr) { |
| 353 | temp_tmgi = list_entry(tmgi_entry_ptr, |
| 354 | struct tmgi_to_clnt_info, |
| 355 | tmgi_list_ptr); |
| 356 | |
| 357 | embms_debug("TMGI entry %d :\n", i); |
| 358 | embms_debug("TMGI multicast addr : %pI4 , port %u\n\n", |
| 359 | &temp_tmgi->tmgi_multicast_addr, |
| 360 | ntohs(temp_tmgi->tmgi_port)); |
| 361 | embms_debug("No of clients : %d\n", |
| 362 | temp_tmgi->no_of_clients); |
| 363 | j = 1; |
| 364 | |
| 365 | list_for_each_safe(clnt_ptr, prev_clnt_ptr, |
| 366 | &temp_tmgi->client_list_head) { |
| 367 | temp_client = list_entry(clnt_ptr, |
| 368 | struct clnt_info, |
| 369 | client_list_ptr); |
| 370 | embms_debug("Client entry %d :\n", j); |
| 371 | embms_debug("client addr : %pI4 , port %u\n\n", |
| 372 | &temp_client->addr, |
| 373 | ntohs(temp_client->port)); |
| 374 | j++; |
| 375 | } |
| 376 | i++; |
| 377 | embms_debug("===========================================\n\n"); |
| 378 | } |
| 379 | } else { |
| 380 | embms_debug("No TMGI entries to Display\n"); |
| 381 | } |
| 382 | embms_debug("==================================================================\n\n"); |
| 383 | } |
| 384 | |
| 385 | /** |
| 386 | * delete_tmgi_entry_from_table() - deletes tmgi from global tmgi-client table |
| 387 | * @buffer: Buffer containing TMGI info for deletion. |
| 388 | * |
| 389 | * This function completely removes the TMGI from |
| 390 | * global TMGI-client table, along with the client list |
| 391 | * so that no packets for this TMGI are processed |
| 392 | * |
| 393 | * Return: Success on deleting TMGI entry, error otherwise. |
| 394 | */ |
| 395 | |
| 396 | int delete_tmgi_entry_from_table(char *buffer) |
| 397 | { |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 398 | struct tmgi_to_clnt_info_update *info_update; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 399 | struct clnt_info *temp_client = NULL; |
| 400 | struct tmgi_to_clnt_info *temp_tmgi = NULL; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 401 | struct list_head *clnt_ptr, *prev_clnt_ptr; |
| 402 | |
| 403 | embms_debug("delete_tmgi_entry_from_table: Enter\n"); |
| 404 | |
| 405 | info_update = (struct tmgi_to_clnt_info_update *)buffer; |
| 406 | |
| 407 | if (!info_update) { |
| 408 | embms_error("delete_tmgi_entry_from_table:"); |
| 409 | embms_error("NULL arguments passed\n"); |
| 410 | return -EBADPARAM; |
| 411 | } |
| 412 | |
| 413 | /* This function is used to delete a specific TMGI entry |
| 414 | * when that particular TMGI goes down |
| 415 | * Search for the TMGI entry in our local table |
| 416 | */ |
| 417 | if (embms_conf.no_of_tmgi_sessions == 0) { |
| 418 | embms_error("TMGI count 0. Nothing to delete\n"); |
| 419 | return SUCCESS; |
| 420 | } |
| 421 | |
| 422 | temp_tmgi = check_for_tmgi_entry(info_update->multicast_addr, |
| 423 | info_update->multicast_port); |
| 424 | |
| 425 | if (!temp_tmgi) { |
| 426 | /* TMGI entry was not found in our local table*/ |
| 427 | embms_error("delete_client_entry_from_table :"); |
| 428 | embms_error("Desired TMGI entry not found\n"); |
| 429 | return -EBADPARAM; |
| 430 | } |
| 431 | |
| 432 | spin_lock_bh(&embms_conf.lock); |
| 433 | |
| 434 | /* We need to free memory allocated to client entries |
| 435 | * for a particular TMGI entry |
| 436 | */ |
| 437 | |
| 438 | list_for_each_safe(clnt_ptr, prev_clnt_ptr, |
| 439 | &temp_tmgi->client_list_head) { |
| 440 | temp_client = list_entry(clnt_ptr, |
| 441 | struct clnt_info, |
| 442 | client_list_ptr); |
| 443 | embms_debug("delete_tmgi_entry_from_table :"); |
| 444 | embms_debug("Client addr to delete :%pI4 , port %u\n", |
| 445 | &temp_client->addr, ntohs(temp_client->port)); |
| 446 | list_del(&temp_client->client_list_ptr); |
| 447 | temp_tmgi->no_of_clients--; |
| 448 | kfree(temp_client); |
| 449 | } |
| 450 | |
| 451 | /* Free memory allocated to tmgi entry*/ |
| 452 | |
| 453 | list_del(&temp_tmgi->tmgi_list_ptr); |
| 454 | kfree(temp_tmgi); |
| 455 | embms_conf.no_of_tmgi_sessions--; |
| 456 | |
| 457 | spin_unlock_bh(&embms_conf.lock); |
| 458 | |
| 459 | embms_debug("delete_tmgi_entry_from_table : TMGI Entry deleted.\n"); |
| 460 | |
| 461 | return SUCCESS; |
| 462 | } |
| 463 | |
| 464 | /** |
| 465 | * delete_client_entry_from_all_tmgi() - deletes client from all tmgi lists |
| 466 | * @buffer: Buffer containing client info for deletion. |
| 467 | * |
| 468 | * This function completely removes a client from |
| 469 | * all TMGIs in global TMGI-client table. Also delets TMGI |
| 470 | * entries if no more clients are there |
| 471 | * |
| 472 | * Return: Success on deleting client entry, error otherwise. |
| 473 | */ |
| 474 | int delete_client_entry_from_all_tmgi(char *buffer) |
| 475 | { |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 476 | struct tmgi_to_clnt_info_update *info_update; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 477 | struct clnt_info *temp_client = NULL; |
| 478 | struct tmgi_to_clnt_info *tmgi = NULL; |
| 479 | struct list_head *tmgi_entry_ptr, *prev_tmgi_entry_ptr; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 480 | |
| 481 | /* We use this function when we want to delete any |
| 482 | * client entry from all TMGI entries. This scenario |
| 483 | * happens when any client disconnects and hence |
| 484 | * we need to clean all realted client entries |
| 485 | * in our mapping table |
| 486 | */ |
| 487 | |
| 488 | embms_debug("del_clnt_from_all_tmgi: Enter\n"); |
| 489 | |
| 490 | info_update = (struct tmgi_to_clnt_info_update *)buffer; |
| 491 | |
| 492 | if (!info_update) { |
| 493 | embms_error("del_clnt_from_all_tmgi:"); |
| 494 | embms_error("NULL arguments passed\n"); |
| 495 | return -EBADPARAM; |
| 496 | } |
| 497 | |
| 498 | /* We start checking from first TMGI entry and if client |
| 499 | * entry is found in client entries of any TMGI, we clean |
| 500 | * up that client entry from that TMGI entry |
| 501 | */ |
| 502 | if (embms_conf.no_of_tmgi_sessions == 0) |
| 503 | return SUCCESS; |
| 504 | |
| 505 | list_for_each_safe(tmgi_entry_ptr, prev_tmgi_entry_ptr, |
| 506 | &tmgi_to_clnt_map_tbl.tmgi_list_ptr) { |
| 507 | tmgi = list_entry(tmgi_entry_ptr, |
| 508 | struct tmgi_to_clnt_info, |
| 509 | tmgi_list_ptr); |
| 510 | |
| 511 | temp_client = chk_clnt_entry(tmgi, info_update); |
| 512 | if (!temp_client) |
| 513 | continue; |
| 514 | |
| 515 | spin_lock_bh(&embms_conf.lock); |
| 516 | |
| 517 | list_del(&temp_client->client_list_ptr); |
| 518 | tmgi->no_of_clients--; |
| 519 | kfree(temp_client); |
| 520 | |
| 521 | spin_unlock_bh(&embms_conf.lock); |
| 522 | |
| 523 | temp_client = NULL; |
| 524 | |
| 525 | if (tmgi->no_of_clients == 0) { |
| 526 | /* Deleted clnt was the only clnt for |
| 527 | * that TMGI we need to delete TMGI |
| 528 | * entry from table |
| 529 | */ |
| 530 | embms_debug("del_clnt_from_all_tmgi:"); |
| 531 | embms_debug("Deleted client was "); |
| 532 | embms_debug("last client for tmgi\n"); |
| 533 | embms_debug("del_clnt_from_all_tmgi:"); |
| 534 | embms_debug("Delting tmgi as it has "); |
| 535 | embms_debug("zero clients.TMGI IP "); |
| 536 | embms_debug(":%pI4 , port %u\n", |
| 537 | &tmgi->tmgi_multicast_addr, |
| 538 | ntohs(tmgi->tmgi_port)); |
| 539 | |
| 540 | spin_lock_bh(&embms_conf.lock); |
| 541 | |
| 542 | list_del(&tmgi->tmgi_list_ptr); |
| 543 | embms_conf.no_of_tmgi_sessions--; |
| 544 | kfree(tmgi); |
| 545 | |
| 546 | spin_unlock_bh(&embms_conf.lock); |
| 547 | |
| 548 | embms_debug("del_clnt_from_all_tmgi:"); |
| 549 | embms_debug("TMGI entry deleted\n"); |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | embms_debug("del_clnt_from_all_tmgi Successful\n"); |
| 554 | return SUCCESS; |
| 555 | } |
| 556 | |
| 557 | /** |
| 558 | * add_client_entry_to_table() - add client entry to specified TMGI |
| 559 | * @buffer: Buffer containing client info for addition. |
| 560 | * |
| 561 | * This function adds a client to the specified TMGI in |
| 562 | * the global TMGI-client table. If TMGI entry is not |
| 563 | * present, it adds a new TMGI entry and adds client |
| 564 | * entry to it. |
| 565 | * |
| 566 | * Return: Success on adding client entry, error otherwise. |
| 567 | */ |
| 568 | int add_client_entry_to_table(char *buffer) |
| 569 | { |
Mohammed Javid | c1dc165 | 2017-11-14 10:11:12 +0530 | [diff] [blame] | 570 | int ret; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 571 | struct tmgi_to_clnt_info_update *info_update; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 572 | struct clnt_info *new_client = NULL; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 573 | struct tmgi_to_clnt_info *tmgi = NULL; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 574 | struct neighbour *neigh_entry; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 575 | |
| 576 | embms_debug("add_client_entry_to_table: Enter\n"); |
| 577 | |
| 578 | info_update = (struct tmgi_to_clnt_info_update *)buffer; |
| 579 | |
| 580 | if (!info_update) { |
| 581 | embms_error("add_client_entry_to_table:"); |
| 582 | embms_error("NULL arguments passed\n"); |
| 583 | return -EBADPARAM; |
| 584 | } |
| 585 | |
| 586 | new_client = kzalloc(sizeof(*new_client), GFP_ATOMIC); |
| 587 | if (!new_client) { |
| 588 | embms_error("add_client_entry_to_table:"); |
| 589 | embms_error("Cannot allocate memory\n"); |
| 590 | return -ENOMEM; |
| 591 | } |
| 592 | |
| 593 | new_client->addr = info_update->client_addr; |
| 594 | new_client->port = info_update->client_port; |
| 595 | |
| 596 | neigh_entry = __ipv4_neigh_lookup(dev_global, |
| 597 | (u32)(new_client->addr)); |
| 598 | if (!neigh_entry) { |
| 599 | embms_error("add_client_entry_to_table :"); |
| 600 | embms_error("Can't find neighbour entry\n"); |
| 601 | kfree(new_client); |
| 602 | return -EBADPARAM; |
| 603 | } |
| 604 | |
| 605 | ether_addr_copy(new_client->dmac, neigh_entry->ha); |
| 606 | |
| 607 | embms_debug("DMAC of client : %pM\n", new_client->dmac); |
| 608 | |
| 609 | embms_debug("add_client_entry_to_table:"); |
| 610 | embms_debug("New client addr :%pI4 , port %u\n", |
| 611 | &info_update->client_addr, |
| 612 | ntohs(info_update->client_port)); |
| 613 | |
| 614 | if (embms_conf.no_of_tmgi_sessions == 0) { |
| 615 | /* TMGI Client mapping table is empty. |
| 616 | * First client entry is being added |
| 617 | */ |
| 618 | |
| 619 | embms_debug("tmgi_to_clnt_map_tbl is empty\n"); |
| 620 | |
| 621 | ret = add_new_tmgi_entry(info_update, new_client); |
| 622 | if (ret != SUCCESS) { |
| 623 | kfree(new_client); |
| 624 | new_client = NULL; |
| 625 | } |
| 626 | |
| 627 | goto exit_add; |
| 628 | } |
| 629 | |
| 630 | /* In this case, table already has some entries |
| 631 | * and we need to search for the specific tmgi entry |
| 632 | * for which client entry is to be added |
| 633 | */ |
| 634 | |
| 635 | tmgi = check_for_tmgi_entry(info_update->multicast_addr, |
| 636 | info_update->multicast_port); |
| 637 | if (tmgi) { |
| 638 | if (chk_clnt_entry(tmgi, info_update)) { |
| 639 | kfree(new_client); |
| 640 | return -ENOEFFECT; |
| 641 | } |
| 642 | |
| 643 | /* Adding client to the client list |
| 644 | * for the specified TMGI |
| 645 | */ |
| 646 | |
| 647 | spin_lock_bh(&embms_conf.lock); |
| 648 | |
| 649 | list_add(&new_client->client_list_ptr, |
| 650 | &tmgi->client_list_head); |
| 651 | tmgi->no_of_clients++; |
| 652 | |
| 653 | spin_unlock_bh(&embms_conf.lock); |
| 654 | |
| 655 | ret = SUCCESS; |
| 656 | } else { |
| 657 | /* TMGI specified in the message was not found in |
| 658 | * mapping table.Hence, we need to add a new entry |
| 659 | * for this TMGI and add the specified client to the client |
| 660 | * list |
| 661 | */ |
| 662 | |
| 663 | embms_debug("TMGI entry not present. Adding tmgi entry\n"); |
| 664 | |
| 665 | ret = add_new_tmgi_entry(info_update, new_client); |
| 666 | if (ret != SUCCESS) { |
| 667 | kfree(new_client); |
| 668 | new_client = NULL; |
| 669 | } |
| 670 | } |
| 671 | |
| 672 | exit_add: |
| 673 | return ret; |
| 674 | } |
| 675 | |
| 676 | /** |
| 677 | * delete_client_entry_from_table() - delete client entry from specified TMGI |
| 678 | * @buffer: Buffer containing client info for deletion. |
| 679 | * |
| 680 | * This function deletes a client from the specified TMGI in |
| 681 | * the global TMGI-client table. If this was the last client |
| 682 | * entry, it also deletes the TMGI entry. |
| 683 | * |
| 684 | * Return: Success on deleting client entry, error otherwise. |
| 685 | */ |
| 686 | int delete_client_entry_from_table(char *buffer) |
| 687 | { |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 688 | struct tmgi_to_clnt_info_update *info_update; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 689 | struct clnt_info *temp_client = NULL; |
| 690 | struct tmgi_to_clnt_info *temp_tmgi = NULL; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 691 | |
| 692 | embms_debug("delete_client_entry_from_table: Enter\n"); |
| 693 | |
| 694 | info_update = (struct tmgi_to_clnt_info_update *)buffer; |
| 695 | |
| 696 | if (!info_update) { |
| 697 | embms_error("delete_client_entry_from_table:"); |
| 698 | embms_error("NULL arguments passed\n"); |
| 699 | return -EBADPARAM; |
| 700 | } |
| 701 | |
| 702 | /* Search for the TMGI entry*/ |
| 703 | if (embms_conf.no_of_tmgi_sessions == 0) |
| 704 | return SUCCESS; |
| 705 | |
| 706 | temp_tmgi = check_for_tmgi_entry(info_update->multicast_addr, |
| 707 | info_update->multicast_port); |
| 708 | |
| 709 | if (!temp_tmgi) { |
| 710 | embms_error("delete_client_entry_from_table:TMGI not found\n"); |
| 711 | return -EBADPARAM; |
| 712 | } |
| 713 | /* Delete client entry for a specific tmgi*/ |
| 714 | |
| 715 | embms_debug("delete_client_entry_from_table:clnt addr :%pI4,port %u\n", |
| 716 | &info_update->client_addr, |
| 717 | ntohs(info_update->client_port)); |
| 718 | |
| 719 | temp_client = chk_clnt_entry(temp_tmgi, info_update); |
| 720 | |
| 721 | if (!temp_client) { |
| 722 | /* Specified client entry was not found in client list |
| 723 | * of specified TMGI |
| 724 | */ |
| 725 | embms_error("delete_client_entry_from_table:Clnt not found\n"); |
| 726 | return -EBADPARAM; |
| 727 | } |
| 728 | |
| 729 | spin_lock_bh(&embms_conf.lock); |
| 730 | |
| 731 | list_del(&temp_client->client_list_ptr); |
| 732 | temp_tmgi->no_of_clients--; |
| 733 | |
| 734 | spin_unlock_bh(&embms_conf.lock); |
| 735 | |
| 736 | kfree(temp_client); |
| 737 | temp_client = NULL; |
| 738 | |
| 739 | embms_debug("delete_client_entry_from_table:Client entry deleted\n"); |
| 740 | |
| 741 | if (temp_tmgi->no_of_clients == 0) { |
| 742 | /* If deleted client was the only client for that TMGI |
| 743 | * we need to delete TMGI entry from table |
| 744 | */ |
| 745 | embms_debug("delete_client_entry_from_table:"); |
| 746 | embms_debug("Deleted client was the last client for tmgi\n"); |
| 747 | embms_debug("delete_client_entry_from_table:"); |
| 748 | embms_debug("Deleting tmgi since it has zero clients\n"); |
| 749 | |
| 750 | spin_lock_bh(&embms_conf.lock); |
| 751 | |
| 752 | list_del(&temp_tmgi->tmgi_list_ptr); |
| 753 | embms_conf.no_of_tmgi_sessions--; |
| 754 | kfree(temp_tmgi); |
| 755 | |
| 756 | spin_unlock_bh(&embms_conf.lock); |
| 757 | |
| 758 | embms_debug("delete_client_entry_from_table: TMGI deleted\n"); |
| 759 | } |
| 760 | |
| 761 | if (embms_conf.no_of_tmgi_sessions == 0) |
| 762 | embms_conf.embms_tunneling_status = TUNNELING_OFF; |
| 763 | |
| 764 | return SUCCESS; |
| 765 | } |
| 766 | |
| 767 | /** |
| 768 | * embms_device_ioctl() - handle IOCTL calls to device |
| 769 | * @file: File descriptor of file opened from userspace process |
| 770 | * @ioctl_num: IOCTL to use |
| 771 | * @ioctl_param: IOCTL parameters/arguments |
| 772 | * |
| 773 | * This function is called whenever a process tries to do |
| 774 | * an ioctl on our device file. As per the IOCTL number, |
| 775 | * it calls various functions to manipulate global |
| 776 | * TMGI-client table |
| 777 | * |
| 778 | * Return: Success if functoin call returns SUCCESS, error otherwise. |
| 779 | */ |
| 780 | |
Mohammed Javid | c1dc165 | 2017-11-14 10:11:12 +0530 | [diff] [blame] | 781 | long embms_device_ioctl(struct file *file, unsigned int ioctl_num, |
| 782 | unsigned long ioctl_param) |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 783 | { |
Mohammed Javid | c1dc165 | 2017-11-14 10:11:12 +0530 | [diff] [blame] | 784 | int ret; |
Ravinder Konka | 94266eb | 2015-10-15 19:55:24 +0530 | [diff] [blame] | 785 | char buffer[BUF_LEN]; |
| 786 | struct in_device *iface_dev; |
| 787 | struct in_ifaddr *iface_info; |
| 788 | struct tmgi_to_clnt_info_update *info_update; |
| 789 | char __user *argp = (char __user *)ioctl_param; |
| 790 | |
| 791 | memset(buffer, 0, BUF_LEN); |
| 792 | |
| 793 | /* Switch according to the ioctl called*/ |
| 794 | switch (ioctl_num) { |
| 795 | case ADD_EMBMS_TUNNEL: |
| 796 | if (copy_from_user(buffer, argp, |
| 797 | sizeof(struct tmgi_to_clnt_info_update))) |
| 798 | return -EFAULT; |
| 799 | |
| 800 | ret = add_client_entry_to_table(buffer); |
| 801 | print_tmgi_to_client_table(); |
| 802 | break; |
| 803 | |
| 804 | case DEL_EMBMS_TUNNEL: |
| 805 | if (copy_from_user(buffer, argp, |
| 806 | sizeof(struct tmgi_to_clnt_info_update))) |
| 807 | return -EFAULT; |
| 808 | |
| 809 | ret = delete_client_entry_from_table(buffer); |
| 810 | print_tmgi_to_client_table(); |
| 811 | break; |
| 812 | |
| 813 | case TMGI_DEACTIVATE: |
| 814 | if (copy_from_user(buffer, argp, |
| 815 | sizeof(struct tmgi_to_clnt_info_update))) |
| 816 | return -EFAULT; |
| 817 | |
| 818 | ret = delete_tmgi_entry_from_table(buffer); |
| 819 | print_tmgi_to_client_table(); |
| 820 | break; |
| 821 | |
| 822 | case CLIENT_DEACTIVATE: |
| 823 | if (copy_from_user(buffer, argp, |
| 824 | sizeof(struct tmgi_to_clnt_info_update))) |
| 825 | return -EFAULT; |
| 826 | |
| 827 | ret = delete_client_entry_from_all_tmgi(buffer); |
| 828 | print_tmgi_to_client_table(); |
| 829 | break; |
| 830 | |
| 831 | case GET_EMBMS_TUNNELING_STATUS: |
| 832 | /* This ioctl is both input (ioctl_param) and |
| 833 | * output (the return value of this function) |
| 834 | */ |
| 835 | embms_debug("Sending tunneling status : %d\n", |
| 836 | embms_conf.embms_tunneling_status); |
| 837 | ret = embms_conf.embms_tunneling_status; |
| 838 | break; |
| 839 | |
| 840 | case START_EMBMS_TUNNEL: |
| 841 | |
| 842 | if (copy_from_user(buffer, argp, |
| 843 | sizeof(struct tmgi_to_clnt_info_update))) |
| 844 | return -EFAULT; |
| 845 | |
| 846 | info_update = (struct tmgi_to_clnt_info_update *)buffer; |
| 847 | embms_conf.embms_data_port = info_update->data_port; |
| 848 | udph_global->source = embms_conf.embms_data_port; |
| 849 | |
| 850 | memset(embms_conf.embms_iface, 0, EMBMS_MAX_IFACE_NAME); |
| 851 | memcpy(embms_conf.embms_iface, info_update->iface_name, |
| 852 | EMBMS_MAX_IFACE_NAME); |
| 853 | |
| 854 | embms_conf.embms_tunneling_status = TUNNELING_ON; |
| 855 | embms_debug("Starting Tunneling. Embms_data_port = %d\n", |
| 856 | ntohs(embms_conf.embms_data_port)); |
| 857 | embms_debug("Embms Data Iface = %s\n", embms_conf.embms_iface); |
| 858 | ret = SUCCESS; |
| 859 | |
| 860 | /*Initialise dev_global to bridge device*/ |
| 861 | dev_global = __dev_get_by_name(&init_net, BRIDGE_IFACE); |
| 862 | if (!dev_global) { |
| 863 | embms_error("Error in getting device info\n"); |
| 864 | ret = FAILURE; |
| 865 | } else { |
| 866 | iface_dev = (struct in_device *)dev_global->ip_ptr; |
| 867 | iface_info = iface_dev->ifa_list; |
| 868 | while (iface_info) { |
| 869 | if (memcmp(iface_info->ifa_label, |
| 870 | BRIDGE_IFACE, |
| 871 | strlen(BRIDGE_IFACE)) == 0) |
| 872 | break; |
| 873 | |
| 874 | iface_info = iface_info->ifa_next; |
| 875 | } |
| 876 | if (iface_info) { |
| 877 | embms_debug("IP address of %s iface is %pI4\n", |
| 878 | BRIDGE_IFACE, |
| 879 | &iface_info->ifa_address); |
| 880 | /*Populate source addr for header*/ |
| 881 | iph_global->saddr = iface_info->ifa_address; |
| 882 | ret = SUCCESS; |
| 883 | } else { |
| 884 | embms_debug("Could not find iface address\n"); |
| 885 | ret = FAILURE; |
| 886 | } |
| 887 | } |
| 888 | |
| 889 | break; |
| 890 | |
| 891 | case STOP_EMBMS_TUNNEL: |
| 892 | |
| 893 | embms_conf.embms_tunneling_status = TUNNELING_OFF; |
| 894 | embms_debug("Stopped Tunneling..\n"); |
| 895 | ret = SUCCESS; |
| 896 | break; |
| 897 | } |
| 898 | |
| 899 | return ret; |
| 900 | } |
| 901 | |
| 902 | /* Module Declarations |
| 903 | * This structure will hold the functions to be called |
| 904 | * when a process does something to the device we |
| 905 | * created. Since a pointer to this structure is kept in |
| 906 | * the devices table, it can't be local to |
| 907 | * init_module. NULL is for unimplemented functions. |
| 908 | */ |
| 909 | static const struct file_operations embms_device_fops = { |
| 910 | .owner = THIS_MODULE, |
| 911 | .open = embms_device_open, |
| 912 | .release = embms_device_release, |
| 913 | .read = NULL, |
| 914 | .write = NULL, |
| 915 | .unlocked_ioctl = embms_device_ioctl, |
| 916 | }; |
| 917 | |
| 918 | static int embms_ioctl_init(void) |
| 919 | { |
| 920 | int ret; |
| 921 | struct device *dev; |
| 922 | |
| 923 | ret = alloc_chrdev_region(&device, 0, dev_num, EMBMS_DEVICE_NAME); |
| 924 | if (ret) { |
| 925 | embms_error("device_alloc err\n"); |
| 926 | goto dev_alloc_err; |
| 927 | } |
| 928 | |
| 929 | embms_class = class_create(THIS_MODULE, EMBMS_DEVICE_NAME); |
| 930 | if (IS_ERR(embms_class)) { |
| 931 | embms_error("class_create err\n"); |
| 932 | goto class_err; |
| 933 | } |
| 934 | |
| 935 | dev = device_create(embms_class, NULL, device, |
| 936 | &embms_conf, EMBMS_DEVICE_NAME); |
| 937 | if (IS_ERR(dev)) { |
| 938 | embms_error("device_create err\n"); |
| 939 | goto device_err; |
| 940 | } |
| 941 | |
| 942 | cdev_init(&embms_device, &embms_device_fops); |
| 943 | ret = cdev_add(&embms_device, device, dev_num); |
| 944 | if (ret) { |
| 945 | embms_error("cdev_add err\n"); |
| 946 | goto cdev_add_err; |
| 947 | } |
| 948 | |
| 949 | embms_debug("ioctl init OK!!\n"); |
| 950 | return 0; |
| 951 | |
| 952 | cdev_add_err: |
| 953 | device_destroy(embms_class, device); |
| 954 | device_err: |
| 955 | class_destroy(embms_class); |
| 956 | class_err: |
| 957 | unregister_chrdev_region(device, dev_num); |
| 958 | dev_alloc_err: |
| 959 | return -ENODEV; |
| 960 | } |
| 961 | |
| 962 | static void embms_ioctl_deinit(void) |
| 963 | { |
| 964 | cdev_del(&embms_device); |
| 965 | device_destroy(embms_class, device); |
| 966 | class_destroy(embms_class); |
| 967 | unregister_chrdev_region(device, dev_num); |
| 968 | } |
| 969 | |
| 970 | /*Initialize the module - Register the misc device*/ |
| 971 | static int __init start_embms(void) |
| 972 | { |
| 973 | int ret = 0; |
| 974 | |
| 975 | iph_global = (struct iphdr *)hdr_buff; |
| 976 | udph_global = (struct udphdr *)(hdr_buff + sizeof(struct iphdr)); |
| 977 | |
| 978 | embms_conf.embms_tunneling_status = TUNNELING_OFF; |
| 979 | embms_conf.no_of_tmgi_sessions = 0; |
| 980 | embms_conf.embms_data_port = 0; |
| 981 | atomic_set(&embms_conf.device_under_use, 0); |
| 982 | atomic_set(&embms_conf.ip_ident, 0); |
| 983 | spin_lock_init(&embms_conf.lock); |
| 984 | |
| 985 | embms_debug("Registering embms device\n"); |
| 986 | |
| 987 | ret = embms_ioctl_init(); |
| 988 | if (ret) { |
| 989 | embms_error("embms device failed to register"); |
| 990 | goto fail_init; |
| 991 | } |
| 992 | |
| 993 | INIT_LIST_HEAD(&tmgi_to_clnt_map_tbl.tmgi_list_ptr); |
| 994 | |
| 995 | memset(hdr_buff, 0, sizeof(struct udphdr) + sizeof(struct iphdr)); |
| 996 | udph_global->check = UDP_CHECKSUM; |
| 997 | iph_global->version = IP_VERSION; |
| 998 | iph_global->ihl = IP_IHL; |
| 999 | iph_global->tos = IP_TOS; |
| 1000 | iph_global->frag_off = IP_FRAG_OFFSET; |
| 1001 | iph_global->ttl = IP_TTL; |
| 1002 | iph_global->protocol = IPPROTO_UDP; |
| 1003 | |
| 1004 | dev_global = NULL; |
| 1005 | |
| 1006 | if (!embms_tm_multicast_recv) |
| 1007 | RCU_INIT_POINTER(embms_tm_multicast_recv, |
| 1008 | handle_multicast_stream); |
| 1009 | |
| 1010 | return ret; |
| 1011 | |
| 1012 | fail_init: |
| 1013 | embms_ioctl_deinit(); |
| 1014 | return ret; |
| 1015 | } |
| 1016 | |
| 1017 | /*Cleanup - unregister the appropriate file from proc*/ |
| 1018 | |
| 1019 | static void __exit stop_embms(void) |
| 1020 | { |
| 1021 | embms_ioctl_deinit(); |
| 1022 | |
| 1023 | if (rcu_dereference(embms_tm_multicast_recv)) |
| 1024 | RCU_INIT_POINTER(embms_tm_multicast_recv, NULL); |
| 1025 | |
| 1026 | embms_debug("unregister_chrdev done\n"); |
| 1027 | } |
| 1028 | |
| 1029 | module_init(start_embms); |
| 1030 | module_exit(stop_embms); |
| 1031 | MODULE_LICENSE("GPL v2"); |