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/* 18 * ecm_nss_bond_notifier.c 19 * Bonding notifier functionality. 20 */ 21 22#include <linux/version.h> 23#include <linux/types.h> 24#include <linux/ip.h> 25#include <linux/tcp.h> 26#include <linux/module.h> 27#include <linux/skbuff.h> 28#include <linux/icmp.h> 29#include <linux/debugfs.h> 30#include <linux/kthread.h> 31#include <linux/pkt_sched.h> 32#include <linux/string.h> 33#include <net/route.h> 34#include <net/ip.h> 35#include <net/tcp.h> 36#include <asm/unaligned.h> 37#include <asm/uaccess.h> /* for put_user */ 38#include <net/ipv6.h> 39#include <linux/inet.h> 40#include <linux/in.h> 41#include <linux/udp.h> 42#include <linux/tcp.h> 43 44#include <linux/inetdevice.h> 45#include <linux/if_arp.h> 46#include <linux/netfilter_ipv4.h> 47#include <linux/netfilter_bridge.h> 48#include <linux/if_bridge.h> 49#include <linux/if_bonding.h> 50#include <net/arp.h> 51#include <net/netfilter/nf_conntrack.h> 52#include <net/netfilter/nf_conntrack_acct.h> 53#include <net/netfilter/nf_conntrack_helper.h> 54#include <net/netfilter/nf_conntrack_l4proto.h> 55#include <net/netfilter/nf_conntrack_l3proto.h> 56#include <net/netfilter/nf_conntrack_zones.h> 57#include <net/netfilter/nf_conntrack_core.h> 58#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> 59#include <net/netfilter/ipv4/nf_defrag_ipv4.h> 60 61/* 62 * Debug output levels 63 * 0 = OFF 64 * 1 = ASSERTS / ERRORS 65 * 2 = 1 + WARN 66 * 3 = 2 + INFO 67 * 4 = 3 + TRACE 68 */ 69#define DEBUG_LEVEL ECM_BOND_NOTIFIER_DEBUG_LEVEL 70 71#include <nss_api_if.h> 72 73#include "ecm_types.h" 74#include "ecm_db_types.h" 75#include "ecm_state.h" 76#include "ecm_tracker.h" 77#include "ecm_classifier.h" 78#include "ecm_front_end_types.h" 79#include "ecm_tracker_udp.h" 80#include "ecm_tracker_tcp.h" 81#include "ecm_tracker_datagram.h" 82#include "ecm_db.h" 83#include "ecm_classifier_default.h" 84#include "ecm_nss_ipv4.h" 85#include "ecm_interface.h" 86// GGG #include "ecm_front_end_ipv6.h" 87 88#define ECM_BOND_ID_INVALID 0xFFFFFFFF 89 90/* 91 * Locking of the classifier - concurrency control 92 */ 93static DEFINE_SPINLOCK(ecm_nss_bond_notifier_lock); /* Protect against SMP access between netfilter, events and private threaded function. */ 94 95/* 96 * Debugfs dentry object. 97 */ 98static struct dentry *ecm_nss_bond_notifier_dentry; 99 100/* 101 * General operational control 102 */ 103static int ecm_nss_bond_notifier_stopped = 0; /* When non-zero further traffic will not be processed */ 104 105/* 106 * ecm_nss_bond_notifier_bond_cb 107 * Bond driver notifier 108 */ 109static struct bond_cb ecm_nss_bond_notifier_bond_cb; 110 111/* 112 * NSS Context for LAG function 113 */ 114static void *ecm_nss_bond_notifier_nss_context = NULL; /* Registration for LAG */ 115 116/* 117 * ecm_nss_bond_notifier_send_lag_state() 118 * Send the currnet LAG state of a physical interface that has changed state in the bonding driver. 119 */ 120static nss_tx_status_t ecm_nss_bond_notifier_send_lag_state(struct nss_ctx_instance *nss_ctx, struct net_device *slave, enum nss_lag_state_change_ev slave_state) 121{ 122 int32_t lagid = 0; 123 int32_t bondid = 0; 124 int32_t slave_ifnum; 125 nss_tx_status_t nss_tx_status; 126 struct nss_lag_msg nm; 127 struct nss_lag_state_change *nlsc = NULL; 128 129 DEBUG_INFO("Send LAG update for: %p (%s)\n", slave, slave->name); 130 131 /* 132 * Can only handle interfaces known to the nss 133 */ 134 slave_ifnum = nss_cmn_get_interface_number(nss_ctx, slave); 135 if (slave_ifnum < 0) { 136 DEBUG_WARN("Not an NSS interface: %p\n", slave); 137 return NSS_TX_FAILURE_BAD_PARAM; 138 } 139 140 /* 141 * Figure out the aggregation id of this slave 142 */ 143 memset(&nm, 0, sizeof(nm)); 144 bondid = bond_get_id(slave->master); 145 if (bondid == ECM_BOND_ID_INVALID) { 146 DEBUG_WARN("Invalid LAG group id 0x%x\n", bondid); 147 return NSS_TX_FAILURE; 148 } 149 150 lagid = bondid + NSS_LAG0_INTERFACE_NUM; 151 152 /* 153 * Construct a message to the NSS to update it 154 */ 155 nss_lag_msg_init(&nm, lagid, 156 NSS_TX_METADATA_LAG_STATE_CHANGE, 157 sizeof(struct nss_lag_state_change), 158 NULL, NULL); 159 160 nlsc = &nm.msg.state; 161 162 nlsc->event = slave_state; 163 164 nlsc->interface = slave_ifnum; 165 166 nss_tx_status = nss_lag_tx(nss_ctx, &nm); 167 if (nss_tx_status != NSS_TX_SUCCESS) { 168 DEBUG_WARN("%p: Send LAG update failed, status: %d\n", slave, nss_tx_status); 169 return NSS_TX_FAILURE; 170 } 171 DEBUG_TRACE("%p: Send LAG update sent\n", slave); 172 return NSS_TX_SUCCESS; 173} 174 175/* 176 * ecm_nss_bond_notifier_bond_release() 177 * Callback when a slave device is released from slavedom and no longer a part of a bonded interface. 178 */ 179static void ecm_nss_bond_notifier_bond_release(struct net_device *slave_dev) 180{ 181 /* 182 * If operations have stopped then do not process event 183 */ 184 DEBUG_INFO("Bond slave release: %p (%s)\n", slave_dev, slave_dev->name); 185 spin_lock_bh(&ecm_nss_bond_notifier_lock); 186 if (unlikely(ecm_nss_bond_notifier_stopped)) { 187 DEBUG_WARN("Ignoring bond release event - stopped\n"); 188 spin_unlock_bh(&ecm_nss_bond_notifier_lock); 189 return; 190 } 191 spin_unlock_bh(&ecm_nss_bond_notifier_lock); 192 ecm_nss_bond_notifier_send_lag_state(ecm_nss_bond_notifier_nss_context, slave_dev, NSS_LAG_RELEASE); 193} 194 195/* 196 * ecm_nss_bond_notifier_bond_enslave() 197 * Callback when a device is enslaved by a LAG master device 198 */ 199static void ecm_nss_bond_notifier_bond_enslave(struct net_device *slave_dev) 200{ 201 /* 202 * If operations have stopped then do not process event 203 */ 204 DEBUG_INFO("Bond slave enslave: %p (%s)\n", slave_dev, slave_dev->name); 205 spin_lock_bh(&ecm_nss_bond_notifier_lock); 206 if (unlikely(ecm_nss_bond_notifier_stopped)) { 207 DEBUG_WARN("Ignoring bond enslave event - stopped\n"); 208 spin_unlock_bh(&ecm_nss_bond_notifier_lock); 209 return; 210 } 211 spin_unlock_bh(&ecm_nss_bond_notifier_lock); 212 ecm_nss_bond_notifier_send_lag_state(ecm_nss_bond_notifier_nss_context, slave_dev, NSS_LAG_ENSLAVE); 213} 214 215/* 216 * ecm_nss_bond_notifier_bond_link_down() 217 * Callback when a link goes down on a LAG slave 218 */ 219static void ecm_nss_bond_notifier_bond_link_down(struct net_device *slave_dev) 220{ 221 struct net_device *master; 222 223 /* 224 * If operations have stopped then do not process event 225 */ 226 spin_lock_bh(&ecm_nss_bond_notifier_lock); 227 if (unlikely(ecm_nss_bond_notifier_stopped)) { 228 DEBUG_WARN("Ignoring bond link down event - stopped\n"); 229 spin_unlock_bh(&ecm_nss_bond_notifier_lock); 230 return; 231 } 232 spin_unlock_bh(&ecm_nss_bond_notifier_lock); 233 234 /* 235 * A net device that is a LAG slave has lost link. 236 * Due to the heiarchical nature of network topologies, this can 237 * change the packet transmit path for any connection that is using 238 * a device that it sitting "higher" in the heirarchy. Regenerate all 239 * connections using the LAG master. 240 */ 241 242 master = ecm_interface_get_and_hold_dev_master(slave_dev); 243 ecm_interface_dev_regenerate_connections(master); 244 dev_put(master); 245} 246 247/* 248 * ecm_bond_notifier_bond_link_up() 249 * Callback when a device is enslaved by a LAG master device 250 */ 251static void ecm_nss_bond_notifier_bond_link_up(struct net_device *slave_dev) 252{ 253 struct net_device *master; 254 255 /* 256 * If operations have stopped then do not process event 257 */ 258 spin_lock_bh(&ecm_nss_bond_notifier_lock); 259 if (unlikely(ecm_nss_bond_notifier_stopped)) { 260 DEBUG_WARN("Ignoring bond enslave event - stopped\n"); 261 spin_unlock_bh(&ecm_nss_bond_notifier_lock); 262 return; 263 } 264 spin_unlock_bh(&ecm_nss_bond_notifier_lock); 265 266 /* 267 * Tricky to handle, this one. 268 * A net device that is a LAG slave has become active. 269 * Due to the heiarchical nature of network topologies, this can change the packet transmit path 270 * for any connection that is using a device that it sitting "higher" in the heirarchy. 271 * Regenerate all connections using the LAG master. 272 */ 273 274 master = ecm_interface_get_and_hold_dev_master(slave_dev); 275 ecm_interface_dev_regenerate_connections(master); 276 dev_put(master); 277} 278 279/* 280 * ecm_nss_bond_notifier_lag_event_cb() 281 * Handle LAG event from the NSS driver 282 */ 283static void ecm_nss_bond_notifier_lag_event_cb(void *if_ctx, struct nss_lag_msg *msg) 284{ 285 /* 286 * If operations have stopped then do not process event 287 */ 288 spin_lock_bh(&ecm_nss_bond_notifier_lock); 289 if (unlikely(ecm_nss_bond_notifier_stopped)) { 290 DEBUG_WARN("Ignoring LAG event event - stopped\n"); 291 spin_unlock_bh(&ecm_nss_bond_notifier_lock); 292 return; 293 } 294 spin_unlock_bh(&ecm_nss_bond_notifier_lock); 295 296 /* 297 * GGG TODO Figure out if there is anything we need to do here, the old CM did nothing.. 298 */ 299 switch (msg->cm.type) 300 { 301 default: 302 DEBUG_INFO("Unknown LAG event from NSS: %d", msg->cm.type); 303 break; 304 } 305} 306 307void ecm_nss_bond_notifier_stop(int num) 308{ 309 ecm_nss_bond_notifier_stopped = num; 310} 311EXPORT_SYMBOL(ecm_nss_bond_notifier_stop); 312 313/* 314 * ecm_nss_bond_notifier_init() 315 */ 316int ecm_nss_bond_notifier_init(struct dentry *dentry) 317{ 318 DEBUG_INFO("ECM Bonding Notifier init\n"); 319 320 ecm_nss_bond_notifier_dentry = debugfs_create_dir("ecm_nss_bond_notifier", dentry); 321 if (!ecm_nss_bond_notifier_dentry) { 322 DEBUG_ERROR("Failed to create ecm bond notifier directory in debugfs\n"); 323 return -1; 324 } 325 326 if (!debugfs_create_u32("stop", S_IRUGO | S_IWUSR, ecm_nss_bond_notifier_dentry, 327 (u32 *)&ecm_nss_bond_notifier_stopped)) { 328 DEBUG_ERROR("Failed to create ecm bond notifier stopped file in debugfs\n"); 329 debugfs_remove_recursive(ecm_nss_bond_notifier_dentry); 330 return -1; 331 } 332 333 /* 334 * Register Link Aggregation interfaces with NSS driver 335 */ 336 ecm_nss_bond_notifier_nss_context = nss_register_lag_if(NSS_LAG0_INTERFACE_NUM, NULL, ecm_nss_bond_notifier_lag_event_cb, NULL); 337 ecm_nss_bond_notifier_nss_context = nss_register_lag_if(NSS_LAG1_INTERFACE_NUM, NULL, ecm_nss_bond_notifier_lag_event_cb, NULL); 338 ecm_nss_bond_notifier_nss_context = nss_register_lag_if(NSS_LAG2_INTERFACE_NUM, NULL, ecm_nss_bond_notifier_lag_event_cb, NULL); 339 ecm_nss_bond_notifier_nss_context = nss_register_lag_if(NSS_LAG3_INTERFACE_NUM, NULL, ecm_nss_bond_notifier_lag_event_cb, NULL); 340 341 /* 342 * Register Link Aggregation callbacks with the bonding driver 343 */ 344 ecm_nss_bond_notifier_bond_cb.bond_cb_link_up = ecm_nss_bond_notifier_bond_link_up; 345 ecm_nss_bond_notifier_bond_cb.bond_cb_link_down = ecm_nss_bond_notifier_bond_link_down; 346 ecm_nss_bond_notifier_bond_cb.bond_cb_release = ecm_nss_bond_notifier_bond_release; 347 ecm_nss_bond_notifier_bond_cb.bond_cb_enslave = ecm_nss_bond_notifier_bond_enslave; 348 bond_register_cb(&ecm_nss_bond_notifier_bond_cb); 349 350 return 0; 351} 352EXPORT_SYMBOL(ecm_nss_bond_notifier_init); 353 354/* 355 * ecm_nss_bond_notifier_exit() 356 */ 357void ecm_nss_bond_notifier_exit(void) 358{ 359 DEBUG_INFO("ECM Bonding Notifier exit\n"); 360 361 /* 362 * Unregister from the bond driver 363 */ 364 bond_unregister_cb(); 365 366 /* 367 * Unregister Link Aggregation interfaces with NSS driver 368 */ 369 nss_unregister_lag_if(NSS_LAG0_INTERFACE_NUM); 370 nss_unregister_lag_if(NSS_LAG1_INTERFACE_NUM); 371 nss_unregister_lag_if(NSS_LAG2_INTERFACE_NUM); 372 nss_unregister_lag_if(NSS_LAG3_INTERFACE_NUM); 373 374 /* 375 * Remove the debugfs files recursively. 376 */ 377 if (ecm_nss_bond_notifier_dentry) { 378 debugfs_remove_recursive(ecm_nss_bond_notifier_dentry); 379 } 380} 381EXPORT_SYMBOL(ecm_nss_bond_notifier_exit); 382