1/* 2 ************************************************************************** 3 * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all copies. 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 ************************************************************************** 15 */ 16 17#include <linux/version.h> 18#include <linux/types.h> 19#include <linux/ip.h> 20#include <linux/tcp.h> 21#include <linux/module.h> 22#include <linux/skbuff.h> 23#include <linux/icmp.h> 24#include <linux/kthread.h> 25#include <linux/debugfs.h> 26#include <linux/pkt_sched.h> 27#include <linux/string.h> 28#include <net/route.h> 29#include <net/ip.h> 30#include <net/tcp.h> 31#include <asm/unaligned.h> 32#include <asm/uaccess.h> /* for put_user */ 33#include <net/ipv6.h> 34#include <linux/inet.h> 35#include <linux/in.h> 36#include <linux/udp.h> 37#include <linux/tcp.h> 38 39#include <linux/netfilter_ipv4.h> 40#include <linux/netfilter_bridge.h> 41#include <net/netfilter/nf_conntrack.h> 42#include <net/netfilter/nf_conntrack_helper.h> 43#include <net/netfilter/nf_conntrack_l4proto.h> 44#include <net/netfilter/nf_conntrack_l3proto.h> 45#include <net/netfilter/nf_conntrack_core.h> 46#include <net/netfilter/nf_conntrack_zones.h> 47#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> 48#include <net/netfilter/ipv4/nf_defrag_ipv4.h> 49#include <net/genetlink.h> 50 51/* 52 * Debug output levels 53 * 0 = OFF 54 * 1 = ASSERTS / ERRORS 55 * 2 = 1 + WARN 56 * 3 = 2 + INFO 57 * 4 = 3 + TRACE 58 */ 59#define DEBUG_LEVEL ECM_CLASSIFIER_NL_DEBUG_LEVEL 60 61#include "ecm_types.h" 62#include "ecm_db_types.h" 63#include "ecm_state.h" 64#include "ecm_tracker.h" 65#include "ecm_classifier.h" 66#include "ecm_front_end_types.h" 67#include "ecm_tracker_udp.h" 68#include "ecm_tracker_tcp.h" 69#include "ecm_classifier_nl.h" 70#include "ecm_db.h" 71#include "ecm_front_end_ipv4.h" 72#ifdef ECM_IPV6_ENABLE 73#include "ecm_front_end_ipv6.h" 74#endif 75 76/* 77 * Magic numbers 78 */ 79#define ECM_CLASSIFIER_NL_INSTANCE_MAGIC 0xFE12 80 81#define ECM_CLASSIFIER_NL_F_ACCEL (1 << 0) /* acceleration requested */ 82#define ECM_CLASSIFIER_NL_F_ACCEL_OK (1 << 1) /* acceleration confirmed */ 83#define ECM_CLASSIFIER_NL_F_CLOSED (1 << 2) /* close event issued */ 84 85/* 86 * struct ecm_classifier_nl_instance 87 * State to allow tracking of dynamic qos for a connection 88 */ 89struct ecm_classifier_nl_instance { 90 struct ecm_classifier_instance base; /* Base type */ 91 92 struct ecm_classifier_nl_instance *next; /* Next classifier state instance (for accouting and reporting purposes) */ 93 struct ecm_classifier_nl_instance *prev; /* Next classifier state instance (for accouting and reporting purposes) */ 94 95 uint32_t ci_serial; /* RO: Serial of the connection */ 96 struct ecm_classifier_process_response process_response;/* Last process response computed */ 97 int refs; /* Integer to trap we never go negative */ 98 unsigned int flags; /* See ECM_CLASSIFIER_NL_F_* */ 99#if (DEBUG_LEVEL > 0) 100 uint16_t magic; 101#endif 102}; 103 104/* 105 * Operational control 106 */ 107static bool ecm_classifier_nl_enabled = false; /* Operational behaviour */ 108 109/* 110 * Management thread control 111 */ 112static bool ecm_classifier_nl_terminate_pending = false; /* True when the user wants us to terminate */ 113 114/* 115 * Debugfs dentry object. 116 */ 117static struct dentry *ecm_classifier_nl_dentry; 118 119/* 120 * Locking of the classifier structures 121 */ 122static DEFINE_SPINLOCK(ecm_classifier_nl_lock); /* Protect SMP access. */ 123 124/* 125 * List of our classifier instances 126 */ 127static struct ecm_classifier_nl_instance *ecm_classifier_nl_instances = NULL; 128 /* list of all active instances */ 129static int ecm_classifier_nl_count = 0; /* Tracks number of instances allocated */ 130 131/* 132 * Listener for db events 133 */ 134struct ecm_db_listener_instance *ecm_classifier_nl_li = NULL; 135 136/* 137 * Generic Netlink family and multicast group names 138 */ 139static struct genl_multicast_group ecm_cl_nl_genl_mcgrp = { 140 .name = ECM_CL_NL_GENL_MCGRP, 141}; 142 143static struct genl_family ecm_cl_nl_genl_family = { 144 .id = GENL_ID_GENERATE, 145 .hdrsize = 0, 146 .name = ECM_CL_NL_GENL_NAME, 147 .version = ECM_CL_NL_GENL_VERSION, 148 .maxattr = ECM_CL_NL_GENL_ATTR_MAX, 149}; 150 151/* 152 * helper for sending basic genl commands requiring only a tuple attribute 153 * 154 * TODO: implement a message queue serviced by a thread to allow automatic 155 * retries for accel_ok and closed messages. 156 */ 157static int 158ecm_classifier_nl_send_genl_msg(enum ECM_CL_NL_GENL_CMD cmd, 159 struct ecm_cl_nl_genl_attr_tuple *tuple) 160{ 161 int ret; 162 void *msg_head; 163 struct sk_buff *skb; 164 165 skb = genlmsg_new(sizeof(*tuple) + ecm_cl_nl_genl_family.hdrsize, 166 GFP_ATOMIC); 167 if (skb == NULL) { 168 DEBUG_WARN("failed to alloc nlmsg\n"); 169 return -ENOMEM; 170 } 171 172 msg_head = genlmsg_put(skb, 173 0, /* netlink PID */ 174 0, /* sequence number */ 175 &ecm_cl_nl_genl_family, 176 0, /* flags */ 177 cmd); 178 if (msg_head == NULL) { 179 DEBUG_WARN("failed to add genl headers\n"); 180 nlmsg_free(skb); 181 return -ENOMEM; 182 } 183 184 ret = nla_put(skb, ECM_CL_NL_GENL_ATTR_TUPLE, sizeof(*tuple), tuple); 185 if (ret != 0) { 186 DEBUG_WARN("failed to put tuple into genl msg: %d\n", ret); 187 nlmsg_free(skb); 188 return ret; 189 } 190 191 ret = genlmsg_end(skb, msg_head); 192 if (ret < 0) { 193 DEBUG_WARN("failed to finalize genl msg: %d\n", ret); 194 nlmsg_free(skb); 195 return ret; 196 } 197 198 /* genlmsg_multicast frees the skb in both success and error cases */ 199 ret = genlmsg_multicast(skb, 0, ecm_cl_nl_genl_mcgrp.id, GFP_ATOMIC); 200 if (ret != 0) { 201 DEBUG_WARN("genl multicast failed: %d\n", ret); 202 return ret; 203 } 204 205 return 0; 206} 207 208/* 209 * ecm_cl_nl_genl_attr_tuple_encode() 210 * Helper function to convert connection IP info into a genl_attr_tuple 211 */ 212static int ecm_cl_nl_genl_attr_tuple_encode(struct ecm_cl_nl_genl_attr_tuple *tuple, 213 int ip_version, 214 int proto, 215 ip_addr_t src_ip, 216 int src_port, 217 ip_addr_t dst_ip, 218 int dst_port) 219{ 220 memset(tuple, 0, sizeof(*tuple)); 221 tuple->proto = (uint8_t)proto; 222 tuple->src_port = htons((uint16_t)src_port); 223 tuple->dst_port = htons((uint16_t)dst_port); 224 if (ip_version == 4) { 225 tuple->af = AF_INET; 226 ECM_IP_ADDR_TO_NIN4_ADDR(tuple->src_ip.in.s_addr, src_ip); 227 ECM_IP_ADDR_TO_NIN4_ADDR(tuple->dst_ip.in.s_addr, dst_ip); 228 return 0; 229 } 230#ifdef ECM_IPV6_ENABLE 231 if (ip_version == 6) { 232 tuple->af = AF_INET6; 233 ECM_IP_ADDR_TO_NIN6_ADDR(tuple->src_ip.in6, src_ip); 234 ECM_IP_ADDR_TO_NIN6_ADDR(tuple->dst_ip.in6, dst_ip); 235 return 0; 236 } 237#endif 238 return -EAFNOSUPPORT; 239} 240 241/* 242 * ecm_cl_nl_genl_attr_tuple_decode() 243 * Helper function to convert a genl_attr_tuple into connection IP info 244 */ 245static int ecm_cl_nl_genl_attr_tuple_decode(struct ecm_cl_nl_genl_attr_tuple *tuple, 246 int *proto, 247 ip_addr_t src_ip, 248 int *src_port, 249 ip_addr_t dst_ip, 250 int *dst_port) 251{ 252 *proto = tuple->proto; 253 *src_port = ntohs(tuple->src_port); 254 *dst_port = ntohs(tuple->dst_port); 255 if (AF_INET == tuple->af) { 256 ECM_NIN4_ADDR_TO_IP_ADDR(src_ip, tuple->src_ip.in.s_addr); 257 ECM_NIN4_ADDR_TO_IP_ADDR(dst_ip, tuple->dst_ip.in.s_addr); 258 return 0; 259 } 260#ifdef ECM_IPV6_ENABLE 261 if (AF_INET6 == tuple->af) { 262 ECM_NIN6_ADDR_TO_IP_ADDR(src_ip, tuple->src_ip.in6); 263 ECM_NIN6_ADDR_TO_IP_ADDR(dst_ip, tuple->dst_ip.in6); 264 return 0; 265 } 266#endif 267 return -EAFNOSUPPORT; 268} 269 270/* 271 * ecm_classifier_nl_genl_msg_ACCEL_OK() 272 * Indicates that Accelleration is okay to the netlink channel 273 */ 274static void ecm_classifier_nl_genl_msg_ACCEL_OK(struct ecm_classifier_nl_instance *cnli) 275{ 276 struct ecm_db_connection_instance *ci; 277 int ret; 278 int ip_version; 279 int proto; 280 int src_port; 281 int dst_port; 282 ip_addr_t src_ip; 283 ip_addr_t dst_ip; 284 struct ecm_cl_nl_genl_attr_tuple tuple; 285 286 /* 287 * Lookup the associated connection 288 */ 289 ci = ecm_db_connection_serial_find_and_ref(cnli->ci_serial); 290 if (!ci) { 291 DEBUG_TRACE("%p: No ci found for %u\n", cnli, cnli->ci_serial); 292 return; 293 } 294 295 spin_lock_bh(&ecm_classifier_nl_lock); 296 297 /* if we've already issued an ACCEL_OK on this connection, 298 do not send it again */ 299 if (cnli->flags & ECM_CLASSIFIER_NL_F_ACCEL_OK) { 300 spin_unlock_bh(&ecm_classifier_nl_lock); 301 ecm_db_connection_deref(ci); 302 return; 303 } 304 305 spin_unlock_bh(&ecm_classifier_nl_lock); 306 307 proto = ecm_db_connection_protocol_get(ci); 308 ecm_db_connection_from_address_get(ci, src_ip); 309 src_port = (uint16_t)ecm_db_connection_from_port_get(ci); 310 ecm_db_connection_to_address_get(ci, dst_ip); 311 dst_port = ecm_db_connection_to_port_get(ci); 312 313 ip_version = ecm_db_connection_ip_version_get(ci); 314 ecm_db_connection_deref(ci); 315 316 ret = ecm_cl_nl_genl_attr_tuple_encode(&tuple, 317 ip_version, 318 proto, 319 src_ip, 320 src_port, 321 dst_ip, 322 dst_port); 323 if (ret != 0) { 324 DEBUG_WARN("failed to encode genl_attr_tuple: %d\n", ret); 325 return; 326 } 327 328 ret = ecm_classifier_nl_send_genl_msg(ECM_CL_NL_GENL_CMD_ACCEL_OK, 329 &tuple); 330 if (ret != 0) { 331 DEBUG_WARN("failed to send ACCEL_OK: %p, serial %u\n", 332 cnli, cnli->ci_serial); 333 return; 334 } 335 336 spin_lock_bh(&ecm_classifier_nl_lock); 337 cnli->flags |= ECM_CLASSIFIER_NL_F_ACCEL_OK; 338 spin_unlock_bh(&ecm_classifier_nl_lock); 339} 340 341/* 342 * ecm_classifier_nl_genl_msg_closed() 343 * Invoke this when the connection has been closed and it has been accelerated previously. 344 * 345 * GGG TODO The purpose of this is not clear, esp. wrt. "accel ok" message. 346 * DO NOT CALL THIS UNLESS ECM_CLASSIFIER_NL_F_ACCEL_OK has been set. 347 */ 348static void ecm_classifier_nl_genl_msg_closed(struct ecm_db_connection_instance *ci, struct ecm_classifier_nl_instance *cnli, 349 int proto, ip_addr_t src_ip, ip_addr_t dst_ip, int src_port, int dst_port) 350{ 351 int ip_version; 352 int ret; 353 struct ecm_cl_nl_genl_attr_tuple tuple; 354 355 spin_lock_bh(&ecm_classifier_nl_lock); 356 cnli->flags |= ECM_CLASSIFIER_NL_F_CLOSED; 357 spin_unlock_bh(&ecm_classifier_nl_lock); 358 359 ip_version = ecm_db_connection_ip_version_get(ci); 360 ret = ecm_cl_nl_genl_attr_tuple_encode(&tuple, 361 ip_version, 362 proto, 363 src_ip, 364 src_port, 365 dst_ip, 366 dst_port); 367 if (ret != 0) { 368 DEBUG_WARN("failed to encode genl_attr_tuple: %d\n", ret); 369 return; 370 } 371 372 ecm_classifier_nl_send_genl_msg(ECM_CL_NL_GENL_CMD_CONNECTION_CLOSED, &tuple); 373} 374 375/* 376 * ecm_classifier_nl_genl_msg_ACCEL() 377 * handles a ECM_CL_NL_ACCEL message 378 */ 379static int ecm_classifier_nl_genl_msg_ACCEL(struct sk_buff *skb, 380 struct genl_info *info) 381{ 382 int ret; 383 struct nlattr *na; 384 struct ecm_cl_nl_genl_attr_tuple *tuple; 385 struct ecm_db_connection_instance *ci; 386 struct ecm_classifier_nl_instance *cnli; 387 388 /* the netlink message comes to us in network order, but ECM 389 stores addresses in host order */ 390 int proto; 391 int src_port; 392 int dst_port; 393 ip_addr_t src_ip; 394 ip_addr_t dst_ip; 395 396 /* 397 * Check if we are enabled 398 */ 399 spin_lock_bh(&ecm_classifier_nl_lock); 400 if (!ecm_classifier_nl_enabled) { 401 spin_unlock_bh(&ecm_classifier_nl_lock); 402 return -ECONNREFUSED; 403 } 404 spin_unlock_bh(&ecm_classifier_nl_lock); 405 406 na = info->attrs[ECM_CL_NL_GENL_ATTR_TUPLE]; 407 tuple = nla_data(na); 408 409 ret = ecm_cl_nl_genl_attr_tuple_decode(tuple, 410 &proto, 411 src_ip, 412 &src_port, 413 dst_ip, 414 &dst_port); 415 if (ret != 0) { 416 DEBUG_WARN("failed to decode genl_attr_tuple: %d\n", ret); 417 return ret; 418 } 419 420 /* 421 * Locate the connection using the tuple given 422 */ 423 DEBUG_TRACE("ACCEL: Lookup connection " 424 ECM_IP_ADDR_OCTAL_FMT ":%d <> " 425 ECM_IP_ADDR_OCTAL_FMT ":%d " 426 "protocol %d\n", 427 ECM_IP_ADDR_TO_OCTAL(src_ip), 428 src_port, 429 ECM_IP_ADDR_TO_OCTAL(dst_ip), 430 dst_port, 431 tuple->proto); 432 ci = ecm_db_connection_find_and_ref(src_ip, 433 dst_ip, 434 proto, 435 src_port, 436 dst_port); 437 if (!ci) { 438 DEBUG_WARN("database connection not found\n"); 439 return -ENOENT; 440 } 441 DEBUG_TRACE("Connection found: %p\n", ci); 442 443 /* 444 * Get the NL classifier for this connection 445 */ 446 cnli = (struct ecm_classifier_nl_instance *) 447 ecm_db_connection_assigned_classifier_find_and_ref(ci, 448 ECM_CLASSIFIER_TYPE_NL); 449 if (!cnli) { 450 ecm_db_connection_deref(ci); 451 return -EUNATCH; 452 } 453 454 /* 455 * Allow acceleration of the connection. This will be done as 456 * packets are processed in the usual way. 457 */ 458 DEBUG_TRACE("Permit accel: %p\n", ci); 459 spin_lock_bh(&ecm_classifier_nl_lock); 460 cnli->process_response.accel_mode = 461 ECM_CLASSIFIER_ACCELERATION_MODE_ACCEL; 462 cnli->flags |= ECM_CLASSIFIER_NL_F_ACCEL; 463 spin_unlock_bh(&ecm_classifier_nl_lock); 464 465 cnli->base.deref((struct ecm_classifier_instance *)cnli); 466 ecm_db_connection_deref(ci); 467 468 return 0; 469} 470 471 472/* 473 * ecm_classifier_nl_ref() 474 * Ref 475 */ 476static void ecm_classifier_nl_ref(struct ecm_classifier_instance *ci) 477{ 478 struct ecm_classifier_nl_instance *cnli; 479 cnli = (struct ecm_classifier_nl_instance *)ci; 480 481 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli); 482 spin_lock_bh(&ecm_classifier_nl_lock); 483 cnli->refs++; 484 DEBUG_TRACE("%p: cnli ref %d\n", cnli, cnli->refs); 485 DEBUG_ASSERT(cnli->refs > 0, "%p: ref wrap\n", cnli); 486 spin_unlock_bh(&ecm_classifier_nl_lock); 487} 488 489/* 490 * ecm_classifier_nl_deref() 491 * Deref 492 */ 493static int ecm_classifier_nl_deref(struct ecm_classifier_instance *ci) 494{ 495 struct ecm_classifier_nl_instance *cnli; 496 cnli = (struct ecm_classifier_nl_instance *)ci; 497 498 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli); 499 500 spin_lock_bh(&ecm_classifier_nl_lock); 501 cnli->refs--; 502 DEBUG_ASSERT(cnli->refs >= 0, "%p: refs wrapped\n", cnli); 503 DEBUG_TRACE("%p: Netlink classifier deref %d\n", cnli, cnli->refs); 504 if (cnli->refs) { 505 int refs = cnli->refs; 506 spin_unlock_bh(&ecm_classifier_nl_lock); 507 return refs; 508 } 509 510 /* 511 * Object to be destroyed 512 */ 513 ecm_classifier_nl_count--; 514 DEBUG_ASSERT(ecm_classifier_nl_count >= 0, "%p: ecm_classifier_nl_count wrap\n", cnli); 515 516 /* 517 * UnLink the instance from our list 518 */ 519 if (cnli->next) { 520 cnli->next->prev = cnli->prev; 521 } 522 if (cnli->prev) { 523 cnli->prev->next = cnli->next; 524 } else { 525 DEBUG_ASSERT(ecm_classifier_nl_instances == cnli, "%p: list bad %p\n", cnli, ecm_classifier_nl_instances); 526 ecm_classifier_nl_instances = cnli->next; 527 } 528 cnli->next = NULL; 529 cnli->prev = NULL; 530 spin_unlock_bh(&ecm_classifier_nl_lock); 531 532 /* 533 * Final 534 */ 535 DEBUG_INFO("%p: Final Netlink classifier instance\n", cnli); 536 kfree(cnli); 537 538 return 0; 539} 540 541void 542ecm_classifier_nl_process_mark(struct ecm_classifier_nl_instance *cnli, 543 uint32_t mark) 544{ 545 bool updated; 546 ecm_front_end_acceleration_mode_t accel_mode; 547 struct ecm_db_connection_instance *ci; 548 struct ecm_front_end_connection_instance *feci; 549 550 updated = false; 551 552 spin_lock_bh(&ecm_classifier_nl_lock); 553 554 /* 555 * If the mark is different to either of the current flow or return qos tags then we override them. 556 * NOTE: This will force a change of the skb priority and also drive through these qos tags in any acceleration rule. 557 */ 558 if ((mark != cnli->process_response.flow_qos_tag) || (mark != cnli->process_response.return_qos_tag)) { 559 cnli->process_response.flow_qos_tag = mark; 560 cnli->process_response.return_qos_tag = mark; 561 cnli->process_response.process_actions |= 562 ECM_CLASSIFIER_PROCESS_ACTION_QOS_TAG; 563 updated = true; 564 } 565 spin_unlock_bh(&ecm_classifier_nl_lock); 566 567 if (!updated) { 568 return; 569 } 570 571 /* 572 * we need to make sure to propagate the new mark to the 573 * accel engine if the connection has been accelerated. to do that, 574 * since there's no way to directly update an offload rule, 575 * we simply decelerate the connection which should result 576 * in a re-acceleration when the next packet is processed 577 * by the front end, thereby applying the new mark. 578 */ 579 580 /* 581 * Lookup the associated connection 582 */ 583 ci = ecm_db_connection_serial_find_and_ref(cnli->ci_serial); 584 if (!ci) { 585 DEBUG_TRACE("%p: No ci found for %u\n", cnli, cnli->ci_serial); 586 return; 587 } 588 feci = ecm_db_connection_front_end_get_and_ref(ci); 589 accel_mode = feci->accel_state_get(feci); 590 if ((accel_mode == ECM_FRONT_END_ACCELERATION_MODE_ACCEL) 591 || (accel_mode == ECM_FRONT_END_ACCELERATION_MODE_ACCEL_PENDING)) { 592 DEBUG_TRACE("%p: mark changed on offloaded connection, decelerate. new mark: 0x%08x\n", 593 cnli, mark); 594 feci->decelerate(feci); 595 } else { 596 DEBUG_TRACE("%p: mark changed on non-offloaded connection. new mark: 0x%08x\n", 597 cnli, mark); 598 } 599 feci->deref(feci); 600 ecm_db_connection_deref(ci); 601} 602EXPORT_SYMBOL(ecm_classifier_nl_process_mark); 603 604/* 605 * ecm_classifier_nl_process() 606 * Process new data for connection 607 */ 608static void ecm_classifier_nl_process(struct ecm_classifier_instance *aci, ecm_tracker_sender_type_t sender, 609 struct ecm_tracker_ip_header *ip_hdr, struct sk_buff *skb, 610 struct ecm_classifier_process_response *process_response) 611{ 612 struct ecm_classifier_nl_instance *cnli; 613 ecm_classifier_relevence_t relevance; 614 bool enabled; 615 struct ecm_db_connection_instance *ci; 616 ecm_front_end_acceleration_mode_t accel_mode; 617 uint32_t became_relevant = 0; 618 619 cnli = (struct ecm_classifier_nl_instance *)aci; 620 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli); 621 622 /* 623 * Have we decided our relevance? If so return our state. 624 */ 625 spin_lock_bh(&ecm_classifier_nl_lock); 626 relevance = cnli->process_response.relevance; 627 if (relevance != ECM_CLASSIFIER_RELEVANCE_MAYBE) { 628 *process_response = cnli->process_response; 629 spin_unlock_bh(&ecm_classifier_nl_lock); 630 return; 631 } 632 633 /* 634 * Decide upon relevance 635 */ 636 enabled = ecm_classifier_nl_enabled; 637 spin_unlock_bh(&ecm_classifier_nl_lock); 638 639 /* 640 * If classifier is enabled, the connection is routed and the front end says it can accel then we are "relevant". 641 * Any other condition and we are not and will stop analysing this connection. 642 */ 643 relevance = ECM_CLASSIFIER_RELEVANCE_NO; 644 ci = ecm_db_connection_serial_find_and_ref(cnli->ci_serial); 645 if (ci) { 646 struct ecm_front_end_connection_instance *feci; 647 feci = ecm_db_connection_front_end_get_and_ref(ci); 648 accel_mode = feci->accel_state_get(feci); 649 feci->deref(feci); 650 if (enabled && ECM_FRONT_END_ACCELERATION_POSSIBLE(accel_mode) && ecm_db_connection_is_routed_get(ci)) { 651 relevance = ECM_CLASSIFIER_RELEVANCE_YES; 652 became_relevant = ecm_db_time_get(); 653 } 654 ecm_db_connection_deref(ci); 655 } 656 657 /* 658 * Return process response 659 */ 660 spin_lock_bh(&ecm_classifier_nl_lock); 661 cnli->process_response.relevance = relevance; 662 cnli->process_response.became_relevant = became_relevant; 663 *process_response = cnli->process_response; 664 spin_unlock_bh(&ecm_classifier_nl_lock); 665} 666 667/* 668 * ecm_classifier_nl_sync_to_v4() 669 * Front end is pushing accel engine state to us 670 */ 671static void ecm_classifier_nl_sync_to_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync) 672{ 673 struct ecm_classifier_nl_instance *cnli; 674 675 if (!(sync->flow_tx_packet_count || sync->return_tx_packet_count)) { 676 /* 677 * Nothing to update. 678 * We only care about flows that are actively being accelerated. 679 */ 680 return; 681 } 682 683 cnli = (struct ecm_classifier_nl_instance *)aci; 684 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed", cnli); 685 686 switch(sync->reason) { 687 case ECM_FRONT_END_IPV4_RULE_SYNC_REASON_FLUSH: 688 /* do nothing */ 689 DEBUG_TRACE("%p: nl_sync_to_v4: SYNC_FLUSH\n", cnli); 690 break; 691 case ECM_FRONT_END_IPV4_RULE_SYNC_REASON_EVICT: 692 /* do nothing */ 693 DEBUG_TRACE("%p: nl_sync_to_v4: SYNC_EVICT\n", cnli); 694 break; 695 case ECM_FRONT_END_IPV4_RULE_SYNC_REASON_DESTROY: 696 DEBUG_TRACE("%p: nl_sync_to_v4: SYNC_DESTROY\n", cnli); 697 break; 698 case ECM_FRONT_END_IPV4_RULE_SYNC_REASON_STATS: 699 DEBUG_TRACE("%p: nl_sync_to_v4: SYNC_STATS\n", cnli); 700 ecm_classifier_nl_genl_msg_ACCEL_OK(cnli); 701 break; 702 default: 703 DEBUG_TRACE("%p: nl_sync_to_v4: unsupported reason\n", cnli); 704 break; 705 } 706} 707 708/* 709 * ecm_classifier_nl_sync_from_v4() 710 * Front end is retrieving accel engine state from us 711 */ 712static void ecm_classifier_nl_sync_from_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc) 713{ 714 struct ecm_classifier_nl_instance *cnli; 715 716 cnli = (struct ecm_classifier_nl_instance *)aci; 717 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed", cnli); 718} 719 720/* 721 * ecm_classifier_nl_sync_to_v6() 722 * Front end is pushing accel engine state to us 723 */ 724static void ecm_classifier_nl_sync_to_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync) 725{ 726 struct ecm_classifier_nl_instance *cnli; 727 728 cnli = (struct ecm_classifier_nl_instance *)aci; 729 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed", cnli); 730 731 if (!(sync->flow_tx_packet_count || sync->return_tx_packet_count)) { 732 /* 733 * No traffic has been accelerated. 734 * Nothing to update. We only care about flows that are actively being accelerated. 735 */ 736 DEBUG_TRACE("%p: No traffic\n", cnli); 737 return; 738 } 739 740 /* 741 * If this sync does NOT contain a final sync then we have seen traffic 742 * and that acceleration is continuing - acceleration is OK 743 */ 744 switch(sync->reason) { 745 case ECM_FRONT_END_IPV6_RULE_SYNC_REASON_FLUSH: 746 /* do nothing */ 747 DEBUG_TRACE("%p: nl_sync_to_v6: SYNC_FLUSH\n", cnli); 748 break; 749 case ECM_FRONT_END_IPV6_RULE_SYNC_REASON_EVICT: 750 /* do nothing */ 751 DEBUG_TRACE("%p: nl_sync_to_v6: SYNC_EVICT\n", cnli); 752 break; 753 case ECM_FRONT_END_IPV6_RULE_SYNC_REASON_DESTROY: 754 /* do nothing */ 755 DEBUG_TRACE("%p: nl_sync_to_v6: SYNC_DESTROY\n", cnli); 756 break; 757 case ECM_FRONT_END_IPV6_RULE_SYNC_REASON_STATS: 758 DEBUG_TRACE("%p: nl_sync_to_v6: SYNC_STATS\n", cnli); 759 ecm_classifier_nl_genl_msg_ACCEL_OK(cnli); 760 break; 761 default: 762 DEBUG_TRACE("%p: nl_sync_to_v6: unsupported reason\n", cnli); 763 break; 764 } 765} 766 767/* 768 * ecm_classifier_nl_sync_from_v6() 769 * Front end is retrieving accel engine state from us 770 */ 771static void ecm_classifier_nl_sync_from_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc) 772{ 773 struct ecm_classifier_nl_instance *cnli; 774 775 cnli = (struct ecm_classifier_nl_instance *)aci; 776 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed", cnli); 777 778} 779 780/* 781 * ecm_classifier_nl_type_get() 782 * Get type of classifier this is 783 */ 784static ecm_classifier_type_t ecm_classifier_nl_type_get(struct ecm_classifier_instance *ci) 785{ 786 struct ecm_classifier_nl_instance *cnli; 787 cnli = (struct ecm_classifier_nl_instance *)ci; 788 789 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli); 790 return ECM_CLASSIFIER_TYPE_NL; 791} 792 793/* 794 * ecm_classifier_nl_last_process_response_get() 795 * Get result code returned by the last process call 796 */ 797static void ecm_classifier_nl_last_process_response_get(struct ecm_classifier_instance *ci, 798 struct ecm_classifier_process_response *process_response) 799{ 800 struct ecm_classifier_nl_instance *cnli; 801 802 cnli = (struct ecm_classifier_nl_instance *)ci; 803 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli); 804 805 spin_lock_bh(&ecm_classifier_nl_lock); 806 *process_response = cnli->process_response; 807 spin_unlock_bh(&ecm_classifier_nl_lock); 808} 809 810/* 811 * ecm_classifier_nl_reclassify_allowed() 812 * Indicate if reclassify is allowed 813 */ 814static bool ecm_classifier_nl_reclassify_allowed(struct ecm_classifier_instance *ci) 815{ 816 struct ecm_classifier_nl_instance *cnli; 817 cnli = (struct ecm_classifier_nl_instance *)ci; 818 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli); 819 820 return true; 821} 822 823/* 824 * ecm_classifier_nl_reclassify() 825 * Reclassify 826 */ 827static void ecm_classifier_nl_reclassify(struct ecm_classifier_instance *ci) 828{ 829 struct ecm_classifier_nl_instance *cnli; 830 cnli = (struct ecm_classifier_nl_instance *)ci; 831 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli); 832 833 /* 834 * Revert back to MAYBE relevant - we will evaluate when we get the next process() call. 835 */ 836 spin_lock_bh(&ecm_classifier_nl_lock); 837 cnli->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_MAYBE; 838 spin_unlock_bh(&ecm_classifier_nl_lock); 839} 840 841#ifdef ECM_STATE_OUTPUT_ENABLE 842/* 843 * ecm_classifier_nl_state_get() 844 * Return state 845 */ 846static int ecm_classifier_nl_state_get(struct ecm_classifier_instance *ci, struct ecm_state_file_instance *sfi) 847{ 848 int result; 849 struct ecm_classifier_nl_instance *cnli; 850 struct ecm_classifier_process_response process_response; 851 852 cnli = (struct ecm_classifier_nl_instance *)ci; 853 DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed", cnli); 854 855 if ((result = ecm_state_prefix_add(sfi, "nl"))) { 856 return result; 857 } 858 859 spin_lock_bh(&ecm_classifier_nl_lock); 860 process_response = cnli->process_response; 861 spin_unlock_bh(&ecm_classifier_nl_lock); 862 863 /* 864 * Output our last process response 865 */ 866 if ((result = ecm_classifier_process_response_state_get(sfi, &process_response))) { 867 return result; 868 } 869 870 return ecm_state_prefix_remove(sfi); 871} 872#endif 873 874/* 875 * ecm_classifier_nl_ct_get_and_ref() 876 * Returns the conntrack entry for an ecm_db_connection_instance. 877 * @param ci The connection instance to be used for the conntrack search. 878 * @return struct nf_conn * A pointer and a reference to a matching conntrack 879 * entry, or NULL if no such entry is found. 880 * @pre The ci instance is not NULL. 881 * @note This function takes a reference to the associated conntrack entry if 882 * it is found, and the caller must nf_ct_put() this entry when done. 883 * @note FIXME: The param ci should be const, but none of the called functions 884 * are declared const. This would be a larger change. 885 */ 886static struct nf_conn *ecm_classifier_nl_ct_get_and_ref(struct ecm_db_connection_instance *ci) 887{ 888 int ip_version; 889 int proto; 890 int src_port; 891 int dst_port; 892 ip_addr_t src_ip; 893 ip_addr_t dst_ip; 894 struct nf_conntrack_tuple_hash *h; 895 struct nf_conntrack_tuple tuple = {}; 896 897 DEBUG_ASSERT(ci != NULL, "ci was NULL for ct lookup"); 898 ip_version = ecm_db_connection_ip_version_get(ci); 899 proto = ecm_db_connection_protocol_get(ci); 900 ecm_db_connection_from_address_get(ci, src_ip); 901 src_port = (uint16_t)ecm_db_connection_from_port_get(ci); 902 ecm_db_connection_to_address_nat_get(ci, dst_ip); 903 dst_port = (uint16_t)ecm_db_connection_to_port_nat_get(ci); 904 905 if (ip_version == 4) { 906 tuple.src.l3num = AF_INET; 907 ECM_IP_ADDR_TO_NIN4_ADDR(tuple.src.u3.ip, src_ip); 908 ECM_IP_ADDR_TO_NIN4_ADDR(tuple.dst.u3.ip, dst_ip); 909 goto ip_check_done; 910 } 911#ifdef ECM_IPV6_ENABLE 912 if (ip_version == 6) { 913 tuple.src.l3num = AF_INET6; 914 ECM_IP_ADDR_TO_NIN6_ADDR(tuple.src.u3.in6, src_ip); 915 ECM_IP_ADDR_TO_NIN6_ADDR(tuple.dst.u3.in6, dst_ip); 916 goto ip_check_done; 917 } 918#endif 919 return NULL; 920 921ip_check_done: 922 tuple.dst.protonum = proto; 923 tuple.src.u.all = htons(src_port); 924 tuple.dst.u.all = htons(dst_port); 925 926 h = nf_conntrack_find_get(&init_net, NF_CT_DEFAULT_ZONE, &tuple); 927 if (NULL == h) { 928 return NULL; 929 } 930 931 return nf_ct_tuplehash_to_ctrack(h); 932} 933 934/* 935 * ecm_classifier_nl_connection_added() 936 * Invoked when a connection is added to the DB 937 */ 938static void ecm_classifier_nl_connection_added(void *arg, struct ecm_db_connection_instance *ci) 939{ 940 struct nf_conn *ct; 941 struct ecm_classifier_instance *classi; 942 struct ecm_classifier_nl_instance *cnli; 943 uint32_t serial __attribute__((unused)) = ecm_db_connection_serial_get(ci); 944 DEBUG_INFO("%p: NL Listener: conn added with serial: %u\n", ci, serial); 945 946 /* 947 * Only handle events if there is an NL classifier attached 948 */ 949 classi = ecm_db_connection_assigned_classifier_find_and_ref(ci, ECM_CLASSIFIER_TYPE_NL); 950 if (NULL == classi) { 951 DEBUG_TRACE("%p: Connection added ignored - no NL classifier\n", ci); 952 return; 953 } 954 cnli = (struct ecm_classifier_nl_instance *)classi; 955 DEBUG_TRACE("%p: added conn, serial: %u, NL classifier: %p\n", ci, 956 serial, classi); 957 958 ct = ecm_classifier_nl_ct_get_and_ref(ci); 959 if (NULL == ct) { 960 DEBUG_TRACE("%p: Connection add skipped - no associated CT entry.\n", ci); 961 goto classi; 962 } 963 DEBUG_TRACE("%p: added conn, serial: %u, NL classifier: %p, CT: %p\n", 964 ci, serial, classi, ct); 965 966 spin_lock_bh(&ecm_classifier_nl_lock); 967 cnli->process_response.flow_qos_tag = ct->mark; 968 cnli->process_response.return_qos_tag = ct->mark; 969 spin_unlock_bh(&ecm_classifier_nl_lock); 970 nf_ct_put(ct); 971 972classi: 973 classi->deref(classi); 974 975 return; 976} 977 978/* 979 * ecm_classifier_nl_connection_removed() 980 * Invoked when a connection is removed from the DB 981 */ 982static void ecm_classifier_nl_connection_removed(void *arg, struct ecm_db_connection_instance *ci) 983{ 984 uint32_t serial __attribute__((unused)) = ecm_db_connection_serial_get(ci); 985 struct ecm_classifier_instance *classi; 986 struct ecm_classifier_nl_instance *cnli; 987 bool accel_ok; 988 int proto; 989 int src_port; 990 int dst_port; 991 ip_addr_t src_ip; 992 ip_addr_t dst_ip; 993 994 DEBUG_INFO("%p: NL Listener: conn removed with serial: %u\n", ci, serial); 995 996 /* 997 * Only handle events if there is an NL classifier attached 998 */ 999 classi = ecm_db_connection_assigned_classifier_find_and_ref(ci, ECM_CLASSIFIER_TYPE_NL); 1000 if (!classi) { 1001 DEBUG_TRACE("%p: Connection removed ignored - no NL classifier\n", ci); 1002 return; 1003 } 1004 1005 cnli = (struct ecm_classifier_nl_instance *)classi; 1006 DEBUG_INFO("%p: removed conn with serial: %u, NL classifier: %p\n", ci, serial, cnli); 1007 1008 /* 1009 * If the connection was accelerated OK then issue a close 1010 */ 1011 spin_lock_bh(&ecm_classifier_nl_lock); 1012 accel_ok = (cnli->flags & ECM_CLASSIFIER_NL_F_ACCEL_OK)? true : false; 1013 spin_unlock_bh(&ecm_classifier_nl_lock); 1014 if (!accel_ok) { 1015 DEBUG_INFO("%p: cnli: %p, accel not ok\n", ci, cnli); 1016 classi->deref(classi); 1017 return; 1018 } 1019 1020 proto = ecm_db_connection_protocol_get(ci); 1021 ecm_db_connection_from_address_get(ci, src_ip); 1022 src_port = (uint16_t)ecm_db_connection_from_port_get(ci); 1023 ecm_db_connection_to_address_get(ci, dst_ip); 1024 dst_port = ecm_db_connection_to_port_get(ci); 1025 1026 DEBUG_INFO("%p: NL classifier: %p, issue Close\n", ci, cnli); 1027 ecm_classifier_nl_genl_msg_closed(ci, cnli, proto, src_ip, dst_ip, src_port, dst_port); 1028 1029 classi->deref(classi); 1030} 1031 1032/* 1033 * ecm_classifier_nl_instance_alloc() 1034 * Allocate an instance of the Netlink classifier 1035 */ 1036struct ecm_classifier_nl_instance *ecm_classifier_nl_instance_alloc(struct ecm_db_connection_instance *ci) 1037{ 1038 struct ecm_classifier_nl_instance *cnli; 1039 1040 /* 1041 * Allocate the instance 1042 */ 1043 cnli = (struct ecm_classifier_nl_instance *)kzalloc(sizeof(struct ecm_classifier_nl_instance), GFP_ATOMIC | __GFP_NOWARN); 1044 if (!cnli) { 1045 DEBUG_WARN("Failed to allocate Netlink instance\n"); 1046 return NULL; 1047 } 1048 1049 DEBUG_SET_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC); 1050 cnli->refs = 1; 1051 cnli->base.process = ecm_classifier_nl_process; 1052 cnli->base.sync_from_v4 = ecm_classifier_nl_sync_from_v4; 1053 cnli->base.sync_to_v4 = ecm_classifier_nl_sync_to_v4; 1054 cnli->base.sync_from_v6 = ecm_classifier_nl_sync_from_v6; 1055 cnli->base.sync_to_v6 = ecm_classifier_nl_sync_to_v6; 1056 cnli->base.type_get = ecm_classifier_nl_type_get; 1057 cnli->base.last_process_response_get = ecm_classifier_nl_last_process_response_get; 1058 cnli->base.reclassify_allowed = ecm_classifier_nl_reclassify_allowed; 1059 cnli->base.reclassify = ecm_classifier_nl_reclassify; 1060#ifdef ECM_STATE_OUTPUT_ENABLE 1061 cnli->base.state_get = ecm_classifier_nl_state_get; 1062#endif 1063 cnli->base.ref = ecm_classifier_nl_ref; 1064 cnli->base.deref = ecm_classifier_nl_deref; 1065 cnli->ci_serial = ecm_db_connection_serial_get(ci); 1066 1067 /* 1068 * Classifier initially denies acceleration. 1069 */ 1070 cnli->process_response.flow_qos_tag = 0; 1071 cnli->process_response.return_qos_tag = 0; 1072 cnli->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_MAYBE; 1073 cnli->process_response.process_actions = 1074 ECM_CLASSIFIER_PROCESS_ACTION_ACCEL_MODE; 1075 cnli->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_NO; 1076 1077 spin_lock_bh(&ecm_classifier_nl_lock); 1078 1079 /* 1080 * Final check if we are pending termination 1081 */ 1082 if (ecm_classifier_nl_terminate_pending) { 1083 spin_unlock_bh(&ecm_classifier_nl_lock); 1084 DEBUG_INFO("%p: Terminating\n", ci); 1085 kfree(cnli); 1086 return NULL; 1087 } 1088 1089 /* 1090 * Link the new instance into our list at the head 1091 */ 1092 cnli->next = ecm_classifier_nl_instances; 1093 if (ecm_classifier_nl_instances) { 1094 ecm_classifier_nl_instances->prev = cnli; 1095 } 1096 ecm_classifier_nl_instances = cnli; 1097 1098 /* 1099 * Increment stats 1100 */ 1101 ecm_classifier_nl_count++; 1102 DEBUG_ASSERT(ecm_classifier_nl_count > 0, "%p: ecm_classifier_nl_count wrap\n", cnli); 1103 spin_unlock_bh(&ecm_classifier_nl_lock); 1104 1105 DEBUG_INFO("Netlink instance alloc: %p\n", cnli); 1106 return cnli; 1107} 1108EXPORT_SYMBOL(ecm_classifier_nl_instance_alloc); 1109 1110/* 1111 * ecm_classifier_nl_set_command() 1112 * Set Netlink command to accel/decel connection. 1113 */ 1114static ssize_t ecm_classifier_nl_set_command(struct file *file, 1115 const char __user *user_buf, 1116 size_t sz, loff_t *ppos) 1117 1118{ 1119#define ECM_CLASSIFIER_NL_SET_IP_COMMAND_FIELDS 7 1120 char *cmd_buf; 1121 int field_count; 1122 char *field_ptr; 1123 char *fields[ECM_CLASSIFIER_NL_SET_IP_COMMAND_FIELDS]; 1124 char cmd; 1125 uint32_t serial; 1126 ip_addr_t src_ip; 1127 uint32_t src_port; 1128 int proto; 1129 ip_addr_t dest_ip; 1130 uint32_t dest_port; 1131 struct ecm_db_connection_instance *ci; 1132 struct ecm_classifier_nl_instance *cnli; 1133 struct ecm_front_end_connection_instance *feci; 1134 1135 /* 1136 * Check if we are enabled 1137 */ 1138 spin_lock_bh(&ecm_classifier_nl_lock); 1139 if (!ecm_classifier_nl_enabled) { 1140 spin_unlock_bh(&ecm_classifier_nl_lock); 1141 return -EINVAL; 1142 } 1143 spin_unlock_bh(&ecm_classifier_nl_lock); 1144 1145 /* 1146 * buf is formed as: 1147 * [0] [1] [2] [3] [4] [5] [6] 1148 * <CMD>/<SERIAL>/<src_ip>/<src_port>/<proto>/<dest_ip>/<dest_port> 1149 * CMD: 1150 * F = Accelerate based on IP address, <SERIAL> unused 1151 * f = Decelerate based on IP address, <SERIAL> unused 1152 * S = Accelerate based on serial, <SERIAL> only becomes relevant 1153 * s = Decelerate based on serial, <SERIAL> only becomes relevant 1154 */ 1155 cmd_buf = (char *)kzalloc(sz + 1, GFP_ATOMIC); 1156 if (!cmd_buf) { 1157 return -ENOMEM; 1158 } 1159 1160 sz = simple_write_to_buffer(cmd_buf, sz, ppos, user_buf, sz); 1161 1162 /* 1163 * Split the buffer into its fields 1164 */ 1165 field_count = 0; 1166 field_ptr = cmd_buf; 1167 fields[field_count] = strsep(&field_ptr, "/"); 1168 while (fields[field_count] != NULL) { 1169 DEBUG_TRACE("FIELD %d: %s\n", field_count, fields[field_count]); 1170 field_count++; 1171 if (field_count == ECM_CLASSIFIER_NL_SET_IP_COMMAND_FIELDS) { 1172 break; 1173 } 1174 fields[field_count] = strsep(&field_ptr, "/"); 1175 } 1176 1177 if (field_count != ECM_CLASSIFIER_NL_SET_IP_COMMAND_FIELDS) { 1178 DEBUG_WARN("invalid field count %d\n", field_count); 1179 kfree(cmd_buf); 1180 return -EINVAL; 1181 } 1182 1183 sscanf(fields[0], "%c", &cmd); 1184 sscanf(fields[1], "%u", &serial); 1185 ecm_string_to_ip_addr(src_ip, fields[2]); 1186 sscanf(fields[3], "%u", &src_port); 1187 sscanf(fields[4], "%d", &proto); 1188 ecm_string_to_ip_addr(dest_ip, fields[5]); 1189 sscanf(fields[6], "%u", &dest_port); 1190 1191 kfree(cmd_buf); 1192 1193 /* 1194 * Locate the connection using the serial or tuple given 1195 */ 1196 switch (cmd) { 1197 case 'F': 1198 case 'f': 1199 DEBUG_TRACE("Lookup connection " ECM_IP_ADDR_OCTAL_FMT ":%d <> " ECM_IP_ADDR_OCTAL_FMT ":%d protocol %d\n", 1200 ECM_IP_ADDR_TO_OCTAL(src_ip), src_port, ECM_IP_ADDR_TO_OCTAL(dest_ip), dest_port, proto); 1201 ci = ecm_db_connection_find_and_ref(src_ip, dest_ip, proto, src_port, dest_port); 1202 break; 1203 case 'S': 1204 case 's': 1205 DEBUG_TRACE("Lookup connection using serial: %u\n", serial); 1206 ci = ecm_db_connection_serial_find_and_ref(serial); 1207 break; 1208 default: 1209 DEBUG_WARN("invalid cmd %c\n", cmd); 1210 return -EINVAL; 1211 } 1212 1213 if (!ci) { 1214 DEBUG_WARN("database connection not found\n"); 1215 return -ENOMEM; 1216 } 1217 DEBUG_TRACE("Connection found: %p\n", ci); 1218 1219 /* 1220 * Get the NL classifier 1221 */ 1222 cnli = (struct ecm_classifier_nl_instance *)ecm_db_connection_assigned_classifier_find_and_ref(ci, ECM_CLASSIFIER_TYPE_NL); 1223 if (!cnli) { 1224 ecm_db_connection_deref(ci); 1225 return -ENOMEM; 1226 } 1227 1228 /* 1229 * Now action the command 1230 */ 1231 switch (cmd) { 1232 case 's': 1233 case 'f': 1234 /* 1235 * Decelerate the connection, NL is denying further accel until it says so. 1236 */ 1237 DEBUG_TRACE("Force decel: %p\n", ci); 1238 spin_lock_bh(&ecm_classifier_nl_lock); 1239 cnli->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_NO; 1240 spin_unlock_bh(&ecm_classifier_nl_lock); 1241 feci = ecm_db_connection_front_end_get_and_ref(ci); 1242 feci->decelerate(feci); 1243 feci->deref(feci); 1244 break; 1245 case 'S': 1246 case 'F': 1247 /* 1248 * Allow acceleration of the connection. This will be done as packets are processed in the usual way. 1249 */ 1250 DEBUG_TRACE("Permit accel: %p\n", ci); 1251 spin_lock_bh(&ecm_classifier_nl_lock); 1252 cnli->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_ACCEL; 1253 cnli->flags |= ECM_CLASSIFIER_NL_F_ACCEL; 1254 spin_unlock_bh(&ecm_classifier_nl_lock); 1255 break; 1256 } 1257 1258 cnli->base.deref((struct ecm_classifier_instance *)cnli); 1259 ecm_db_connection_deref(ci); 1260 1261 return sz; 1262} 1263 1264/* 1265 * ecm_classifier_nl_rule_get_enabled() 1266 */ 1267static int ecm_classifier_nl_rule_get_enabled(void *data, u64 *val) 1268{ 1269 DEBUG_TRACE("get enabled\n"); 1270 1271 *val = ecm_classifier_nl_enabled; 1272 1273 return 0; 1274} 1275 1276/* 1277 * ecm_classifier_nl_rule_set_enabled() 1278 */ 1279static int ecm_classifier_nl_rule_set_enabled(void *data, u64 val) 1280{ 1281 bool prev_state; 1282 1283 /* 1284 * Boolean-ise 1285 */ 1286 if (val) { 1287 val = true; 1288 } 1289 DEBUG_TRACE("ecm_classifier_nl_enabled = %d\n", (int)val); 1290 1291 /* 1292 * Operate under our locks 1293 */ 1294 spin_lock_bh(&ecm_classifier_nl_lock); 1295 prev_state = ecm_classifier_nl_enabled; 1296 ecm_classifier_nl_enabled = (bool)val; 1297 spin_unlock_bh(&ecm_classifier_nl_lock); 1298 1299 /* 1300 * If there is a change in operating state, and that change is that we are now disabled 1301 * then we need to re-generate all connections relevant to this classifier type 1302 */ 1303 if (!val && (prev_state ^ (bool)val)) { 1304 /* 1305 * Change in state to become disabled. 1306 */ 1307#ifdef ECM_DB_CTA_TRACK_ENABLE 1308 ecm_db_connection_regenerate_by_assignment_type(ECM_CLASSIFIER_TYPE_NL); 1309#else 1310 ecm_db_regeneration_needed(); 1311#endif 1312 } 1313 return 0; 1314} 1315 1316/* 1317 * Debugfs attribute for the nl classifier enabled flag. 1318 */ 1319DEFINE_SIMPLE_ATTRIBUTE(ecm_classifier_nl_enabled_fops, ecm_classifier_nl_rule_get_enabled, ecm_classifier_nl_rule_set_enabled, "%llu\n"); 1320 1321/* 1322 * File operations for nl classifier command. 1323 */ 1324static struct file_operations ecm_classifier_nl_cmd_fops = { 1325 .write = ecm_classifier_nl_set_command, 1326}; 1327 1328/* 1329 * Generic Netlink attr checking policies 1330 */ 1331static struct nla_policy 1332ecm_cl_nl_genl_policy[ECM_CL_NL_GENL_ATTR_COUNT] = { 1333 [ECM_CL_NL_GENL_ATTR_TUPLE] = { 1334 .type = NLA_UNSPEC, 1335 .len = sizeof(struct ecm_cl_nl_genl_attr_tuple), }, 1336}; 1337 1338/* 1339 * Generic Netlink message-to-handler mapping 1340 */ 1341static struct genl_ops ecm_cl_nl_genl_ops[] = { 1342 { 1343 .cmd = ECM_CL_NL_GENL_CMD_ACCEL, 1344 .flags = 0, 1345 .policy = ecm_cl_nl_genl_policy, 1346 .doit = ecm_classifier_nl_genl_msg_ACCEL, 1347 .dumpit = NULL, 1348 }, 1349 { 1350 .cmd = ECM_CL_NL_GENL_CMD_ACCEL_OK, 1351 .flags = 0, 1352 .policy = ecm_cl_nl_genl_policy, 1353 .doit = NULL, 1354 .dumpit = NULL, 1355 }, 1356 { 1357 .cmd = ECM_CL_NL_GENL_CMD_CONNECTION_CLOSED, 1358 .flags = 0, 1359 .policy = ecm_cl_nl_genl_policy, 1360 .doit = NULL, 1361 .dumpit = NULL, 1362 }, 1363}; 1364 1365static int ecm_classifier_nl_register_genl(void) 1366{ 1367 int result; 1368 1369 result = genl_register_family(&ecm_cl_nl_genl_family); 1370 if (result != 0) { 1371 DEBUG_ERROR("failed to register genl family: %d\n", result); 1372 goto err1; 1373 } 1374 1375 result = genl_register_ops(&ecm_cl_nl_genl_family, 1376 ecm_cl_nl_genl_ops); 1377 if (result != 0) { 1378 DEBUG_ERROR("failed to register genl ops: %d\n", result); 1379 goto err2; 1380 } 1381 1382 result = genl_register_mc_group(&ecm_cl_nl_genl_family, 1383 &ecm_cl_nl_genl_mcgrp); 1384 if (result != 0) { 1385 DEBUG_ERROR("failed to register genl multicast group: %d\n", 1386 result); 1387 goto err3; 1388 } 1389 1390 return 0; 1391 1392err3: 1393 genl_unregister_ops(&ecm_cl_nl_genl_family, ecm_cl_nl_genl_ops); 1394err2: 1395 genl_unregister_family(&ecm_cl_nl_genl_family); 1396err1: 1397 return result; 1398} 1399 1400static void ecm_classifier_nl_unregister_genl(void) 1401{ 1402 genl_unregister_ops(&ecm_cl_nl_genl_family, ecm_cl_nl_genl_ops); 1403 genl_unregister_family(&ecm_cl_nl_genl_family); 1404} 1405 1406/* 1407 * ecm_classifier_nl_rules_init() 1408 */ 1409int ecm_classifier_nl_rules_init(struct dentry *dentry) 1410{ 1411 int result; 1412 DEBUG_INFO("Netlink classifier Module init\n"); 1413 1414 ecm_classifier_nl_dentry = debugfs_create_dir("ecm_classifier_nl", dentry); 1415 if (!ecm_classifier_nl_dentry) { 1416 DEBUG_ERROR("Failed to create ecm nl classifier directory in debugfs\n"); 1417 return -1; 1418 } 1419 1420 if (!debugfs_create_file("enabled", S_IRUGO | S_IWUSR, ecm_classifier_nl_dentry, 1421 NULL, &ecm_classifier_nl_enabled_fops)) { 1422 DEBUG_ERROR("Failed to create ecm nl classifier enabled file in debugfs\n"); 1423 debugfs_remove_recursive(ecm_classifier_nl_dentry); 1424 return -1; 1425 } 1426 1427 if (!debugfs_create_file("cmd", S_IRUGO | S_IWUSR, ecm_classifier_nl_dentry, 1428 NULL, &ecm_classifier_nl_cmd_fops)) { 1429 DEBUG_ERROR("Failed to create ecm nl classifier cmd file in debugfs\n"); 1430 debugfs_remove_recursive(ecm_classifier_nl_dentry); 1431 return -1; 1432 } 1433 1434 result = ecm_classifier_nl_register_genl(); 1435 if (result) { 1436 DEBUG_ERROR("Failed to register genl sockets\n"); 1437 return result; 1438 } 1439 1440 /* 1441 * Allocate listener instance to listen for db events 1442 */ 1443 ecm_classifier_nl_li = ecm_db_listener_alloc(); 1444 if (!ecm_classifier_nl_li) { 1445 DEBUG_ERROR("Failed to allocate listener\n"); 1446 return -1; 1447 } 1448 1449 /* 1450 * Add the listener into the database 1451 * NOTE: Ref the thread count for the listener 1452 */ 1453 ecm_db_listener_add(ecm_classifier_nl_li, 1454 NULL /* ecm_classifier_nl_iface_added */, 1455 NULL /* ecm_classifier_nl_iface_removed */, 1456 NULL /* ecm_classifier_nl_node_added */, 1457 NULL /* ecm_classifier_nl_node_removed */, 1458 NULL /* ecm_classifier_nl_host_added */, 1459 NULL /* ecm_classifier_nl_host_removed */, 1460 NULL /* ecm_classifier_nl_mapping_added */, 1461 NULL /* ecm_classifier_nl_mapping_removed */, 1462 ecm_classifier_nl_connection_added, 1463 ecm_classifier_nl_connection_removed, 1464 NULL /* ecm_classifier_nl_listener_final */, 1465 ecm_classifier_nl_li); 1466 1467 return 0; 1468} 1469EXPORT_SYMBOL(ecm_classifier_nl_rules_init); 1470 1471/* 1472 * ecm_classifier_nl_rules_exit() 1473 */ 1474void ecm_classifier_nl_rules_exit(void) 1475{ 1476 DEBUG_INFO("Netlink classifier Module exit\n"); 1477 1478 /* 1479 * Release our ref to the listener. 1480 * This will cause it to be unattached to the db listener list. 1481 * GGG TODO THIS IS TOTALLY BROKEN (DUE TO REF COUNT HANDLING NOT BEING HONOURED) 1482 */ 1483 if (ecm_classifier_nl_li) { 1484 ecm_db_listener_deref(ecm_classifier_nl_li); 1485 ecm_classifier_nl_li = NULL; 1486 } 1487 1488 spin_lock_bh(&ecm_classifier_nl_lock); 1489 ecm_classifier_nl_terminate_pending = true; 1490 spin_unlock_bh(&ecm_classifier_nl_lock); 1491 1492 ecm_classifier_nl_unregister_genl(); 1493 1494 /* 1495 * Remove the debugfs files recursively. 1496 */ 1497 if (ecm_classifier_nl_dentry) { 1498 debugfs_remove_recursive(ecm_classifier_nl_dentry); 1499 } 1500} 1501EXPORT_SYMBOL(ecm_classifier_nl_rules_exit); 1502