blob: 862ac73aa935f9100d6b159f8855ed66d70234c3 [file] [log] [blame]
Matthew McClintock6f29aa12013-11-06 15:49:01 -06001/*
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06002 * fast-classifier.c
3 * Shortcut forwarding engine connection manager.
4 * fast-classifier style
5 *
Matthew McClintocka3221942014-01-16 11:44:26 -06006 * Copyright (c) 2013 Qualcomm Atheros, Inc.
7 *
8 * All Rights Reserved.
9 * Qualcomm Atheros Confidential and Proprietary.
Matthew McClintock6f29aa12013-11-06 15:49:01 -060010 */
Matthew McClintock6f29aa12013-11-06 15:49:01 -060011#include <linux/module.h>
Matthew McClintocke1bcfe42013-11-22 15:33:09 -060012#include <linux/sysfs.h>
13#include <linux/skbuff.h>
14#include <net/route.h>
15#include <linux/inetdevice.h>
16#include <linux/netfilter_bridge.h>
17#include <net/netfilter/nf_conntrack_acct.h>
18#include <net/netfilter/nf_conntrack_helper.h>
19#include <net/netfilter/nf_conntrack_zones.h>
20#include <net/netfilter/nf_conntrack_core.h>
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -060021#include <net/genetlink.h>
Matthew McClintockea00adf2013-11-25 19:24:30 -060022#include <linux/list.h>
23#include <linux/spinlock.h>
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -060024
Matthew McClintocke1bcfe42013-11-22 15:33:09 -060025#include "../shortcut-fe/sfe.h"
26#include "../shortcut-fe/sfe_ipv4.h"
Matthew McClintock3abf38e2014-01-07 17:37:56 -060027#include "fast-classifier.h"
Matthew McClintocke1bcfe42013-11-22 15:33:09 -060028
29/*
30 * Per-module structure.
31 */
32struct fast_classifier {
33 spinlock_t lock; /* Lock for SMP correctness */
34
35 /*
36 * Control state.
37 */
38 struct kobject *sys_fast_classifier; /* sysfs linkage */
39
40 /*
41 * Callback notifiers.
42 */
43 struct notifier_block dev_notifier;
44 /* Device notifier */
45 struct notifier_block inet_notifier;
46 /* IP notifier */
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -060047};
Matthew McClintocke1bcfe42013-11-22 15:33:09 -060048
49struct fast_classifier __sc;
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -060050
51static struct nla_policy fast_classifier_genl_policy[FAST_CLASSIFIER_A_MAX + 1] = {
Matthew McClintocke4f9a672014-01-06 17:04:04 -060052 [FAST_CLASSIFIER_A_TUPLE] = { .type = NLA_UNSPEC,
53 .len = sizeof(struct fast_classifier_tuple)
54 },
55};
56
57static struct genl_multicast_group fast_classifier_genl_mcgrp = {
58 .name = FAST_CLASSIFIER_GENL_MCGRP,
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -060059};
60
61static struct genl_family fast_classifier_gnl_family = {
62 .id = GENL_ID_GENERATE,
Matthew McClintock28d75572014-01-03 11:54:08 -060063 .hdrsize = FAST_CLASSIFIER_GENL_HDRSIZE,
64 .name = FAST_CLASSIFIER_GENL_NAME,
65 .version = FAST_CLASSIFIER_GENL_VERSION,
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -060066 .maxattr = FAST_CLASSIFIER_A_MAX,
67};
68
Matthew McClintocke4f9a672014-01-06 17:04:04 -060069static int fast_classifier_offload_genl_msg(struct sk_buff *skb, struct genl_info *info);
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -060070
Matthew McClintocke4f9a672014-01-06 17:04:04 -060071static struct genl_ops fast_classifier_gnl_ops[] = {
72 {
73 .cmd = FAST_CLASSIFIER_C_OFFLOAD,
74 .flags = 0,
75 .policy = fast_classifier_genl_policy,
76 .doit = fast_classifier_offload_genl_msg,
77 .dumpit = NULL,
78 },
79 {
80 .cmd = FAST_CLASSIFIER_C_OFFLOADED,
81 .flags = 0,
82 .policy = fast_classifier_genl_policy,
83 .doit = NULL,
84 .dumpit = NULL,
85 },
86 {
87 .cmd = FAST_CLASSIFIER_C_DONE,
88 .flags = 0,
89 .policy = fast_classifier_genl_policy,
90 .doit = NULL,
91 .dumpit = NULL,
92 },
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -060093};
94
Matthew McClintock75e75fb2014-01-17 14:26:17 -060095atomic_t offload_msgs = ATOMIC_INIT(0);
96atomic_t offload_no_match_msgs = ATOMIC_INIT(0);
97atomic_t offloaded_msgs = ATOMIC_INIT(0);
98atomic_t done_msgs = ATOMIC_INIT(0);
Matthew McClintocke1bcfe42013-11-22 15:33:09 -060099/*
100 * Expose the hook for the receive processing.
101 */
102extern int (*athrs_fast_nat_recv)(struct sk_buff *skb);
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -0600103
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600104/*
105 * Expose what should be a static flag in the TCP connection tracker.
106 */
107extern int nf_ct_tcp_no_window_check;
108
109/*
110 * fast_classifier_recv()
111 * Handle packet receives.
112 *
113 * Returns 1 if the packet is forwarded or 0 if it isn't.
114 */
115int fast_classifier_recv(struct sk_buff *skb)
116{
117 struct net_device *dev;
118#if (SFE_HOOK_ABOVE_BRIDGE)
119 struct in_device *in_dev;
120#endif
121
122 /*
123 * We know that for the vast majority of packets we need the transport
124 * layer header so we may as well start to fetch it now!
125 */
126 prefetch(skb->data + 32);
127 barrier();
128
129 dev = skb->dev;
130
131#if (SFE_HOOK_ABOVE_BRIDGE)
132 /*
133 * Does our input device support IP processing?
134 */
135 in_dev = (struct in_device *)dev->ip_ptr;
136 if (unlikely(!in_dev)) {
137 DEBUG_TRACE("no IP processing for device: %s\n", dev->name);
138 return 0;
139 }
140
141 /*
142 * Does it have an IP address? If it doesn't then we can't do anything
143 * interesting here!
144 */
145 if (unlikely(!in_dev->ifa_list)) {
146 DEBUG_TRACE("no IP address for device: %s\n", dev->name);
147 return 0;
148 }
149#endif
150
151 /*
152 * We're only interested in IP packets.
Nicolas Costaac2979c2014-01-14 10:35:24 -0600153 */
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600154 if (likely(htons(ETH_P_IP) == skb->protocol)) {
155 return sfe_ipv4_recv(dev, skb);
156 }
157
Matthew McClintocka8ad7962014-01-16 16:49:30 -0600158 DEBUG_TRACE("not IP packet\n");
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -0600159 return 0;
160}
Matthew McClintock6f29aa12013-11-06 15:49:01 -0600161
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600162/*
163 * fast_classifier_find_mac_addr()
164 * Find the MAC address for a given IPv4 address.
165 *
166 * Returns true if we find the MAC address, otherwise false.
167 *
168 * We look up the rtable entry for the address and, from its neighbour
169 * structure, obtain the hardware address. This means this function also
170 * works if the neighbours are routers too.
171 */
172static bool fast_classifier_find_mac_addr(uint32_t addr, uint8_t *mac_addr)
Matthew McClintock6f29aa12013-11-06 15:49:01 -0600173{
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600174 struct neighbour *neigh;
175 struct rtable *rt;
176 struct dst_entry *dst;
177 struct net_device *dev;
178
179 /*
180 * Look up the rtable entry for the IP address then get the hardware
181 * address from its neighbour structure. This means this work when the
182 * neighbours are routers too.
183 */
184 rt = ip_route_output(&init_net, addr, 0, 0, 0);
185 if (unlikely(IS_ERR(rt))) {
186 return false;
187 }
188
189 dst = (struct dst_entry *)rt;
190
191 rcu_read_lock();
192 neigh = dst_get_neighbour_noref(dst);
193 if (unlikely(!neigh)) {
194 rcu_read_unlock();
195 dst_release(dst);
Nicolas Costaac2979c2014-01-14 10:35:24 -0600196 return false;
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600197 }
198
199 if (unlikely(!(neigh->nud_state & NUD_VALID))) {
200 rcu_read_unlock();
201 dst_release(dst);
202 return false;
203 }
204
205 dev = neigh->dev;
206 if (!dev) {
207 rcu_read_unlock();
208 dst_release(dst);
209 return false;
210 }
211
212 memcpy(mac_addr, neigh->ha, (size_t)dev->addr_len);
213 rcu_read_unlock();
214
215 dst_release(dst);
216
217 /*
218 * We're only interested in unicast MAC addresses - if it's not a unicast
219 * address then our IP address mustn't be unicast either.
220 */
221 if (is_multicast_ether_addr(mac_addr)) {
222 DEBUG_TRACE("MAC is non-unicast - ignoring\n");
223 return false;
224 }
225
226 return true;
227}
228
Matthew McClintockea00adf2013-11-25 19:24:30 -0600229static DEFINE_SPINLOCK(sfe_connections_lock);
230
231struct sfe_connection {
232 struct list_head list;
233 struct sfe_ipv4_create *sic;
234 struct nf_conn *ct;
Matthew McClintockc5739382013-12-02 14:17:46 -0600235 int hits;
Matthew McClintock55c86982013-12-02 14:24:24 -0600236 int offloaded;
Matthew McClintockd180a7d2014-01-17 11:21:35 -0600237 unsigned char smac[ETH_ALEN];
238 unsigned char dmac[ETH_ALEN];
Matthew McClintockea00adf2013-11-25 19:24:30 -0600239};
Matthew McClintock75e75fb2014-01-17 14:26:17 -0600240static int sfe_connections_size = 0;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600241
242static LIST_HEAD(sfe_connections);
243
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600244/*
Matthew McClintockc5739382013-12-02 14:17:46 -0600245 * fast_classifier_update_protocol()
246 * Update sfe_ipv4_create struct with new protocol information before we offload
247 */
248static int fast_classifier_update_protocol(struct sfe_ipv4_create *p_sic, struct nf_conn *ct)
249{
250 switch (p_sic->protocol) {
251 case IPPROTO_TCP:
252 p_sic->src_td_window_scale = ct->proto.tcp.seen[0].td_scale;
253 p_sic->src_td_max_window = ct->proto.tcp.seen[0].td_maxwin;
254 p_sic->src_td_end = ct->proto.tcp.seen[0].td_end;
255 p_sic->src_td_max_end = ct->proto.tcp.seen[0].td_maxend;
256 p_sic->dest_td_window_scale = ct->proto.tcp.seen[1].td_scale;
257 p_sic->dest_td_max_window = ct->proto.tcp.seen[1].td_maxwin;
258 p_sic->dest_td_end = ct->proto.tcp.seen[1].td_end;
259 p_sic->dest_td_max_end = ct->proto.tcp.seen[1].td_maxend;
260 if (nf_ct_tcp_no_window_check
261 || (ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_BE_LIBERAL)
262 || (ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_BE_LIBERAL)) {
263 p_sic->flags |= SFE_IPV4_CREATE_FLAG_NO_SEQ_CHECK;
264 }
265
266 /*
267 * If the connection is shutting down do not manage it.
268 * state can not be SYN_SENT, SYN_RECV because connection is assured
269 * Not managed states: FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT, CLOSE.
270 */
271 spin_lock(&ct->lock);
272 if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) {
273 spin_unlock(&ct->lock);
274 DEBUG_TRACE("connection in termination state: %#x, s: %pI4:%u, d: %pI4:%u\n",
275 ct->proto.tcp.state, &p_sic->src_ip, ntohs(p_sic->src_port),
276 &p_sic->dest_ip, ntohs(p_sic->dest_port));
277 return 0;
278 }
279 spin_unlock(&ct->lock);
280 break;
281
282 case IPPROTO_UDP:
283 break;
284
285 default:
286 DEBUG_TRACE("unhandled protocol %d\n", p_sic->protocol);
287 return 0;
288 }
289
290 return 1;
291}
292
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600293/* fast_classifier_send_genl_msg()
294 * Function to send a generic netlink message
295 */
296static void fast_classifier_send_genl_msg(int msg, struct fast_classifier_tuple *fc_msg) {
297 struct sk_buff *skb;
298 int rc;
299 void *msg_head;
300
301 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
302 if (skb == NULL)
303 return;
304
305 msg_head = genlmsg_put(skb, 0, 0, &fast_classifier_gnl_family, 0, msg);
306 if (msg_head == NULL) {
307 nlmsg_free(skb);
308 return;
309 }
310
311 rc = nla_put(skb, FAST_CLASSIFIER_A_TUPLE, sizeof(struct fast_classifier_tuple), fc_msg);
312 if (rc != 0) {
313 genlmsg_cancel(skb, msg_head);
314 nlmsg_free(skb);
315 return;
316 }
317
318 rc = genlmsg_end(skb, msg_head);
319 if (rc < 0) {
320 genlmsg_cancel(skb, msg_head);
321 nlmsg_free(skb);
322 return;
323 }
324 genlmsg_multicast(skb, 0, fast_classifier_genl_mcgrp.id, GFP_ATOMIC);
325
Matthew McClintockd180a7d2014-01-17 11:21:35 -0600326 DEBUG_TRACE("INFO: %d : %d, %pI4, %pI4, %d, %d SMAC=%pM DMAC=%pM\n",
Matthew McClintockcf157352014-01-10 16:35:40 -0600327 msg, fc_msg->proto,
328 &(fc_msg->src_saddr),
329 &(fc_msg->dst_saddr),
330 fc_msg->sport, fc_msg->dport,
Matthew McClintockd180a7d2014-01-17 11:21:35 -0600331 fc_msg->smac,
332 fc_msg->dmac);
Matthew McClintock75e75fb2014-01-17 14:26:17 -0600333
334 switch (msg) {
335 case FAST_CLASSIFIER_C_OFFLOADED:
336 atomic_inc(&offloaded_msgs);
337 break;
338 case FAST_CLASSIFIER_C_DONE:
339 atomic_inc(&done_msgs);
340 break;
341 default:
342 DEBUG_ERROR("fast-classifer: Unknown message type sent!\n");
343 break;
344 }
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600345}
346
Matthew McClintockc5739382013-12-02 14:17:46 -0600347/*
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600348 * fast_classifier_offload_genl_msg()
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600349 * Called from user space to offload a connection
350 */
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600351static int fast_classifier_offload_genl_msg(struct sk_buff *skb, struct genl_info *info)
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600352{
Nicolas Costa514fde02014-01-13 15:50:29 -0600353 int ret;
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600354 struct nlattr *na;
Matthew McClintock28d75572014-01-03 11:54:08 -0600355 struct fast_classifier_tuple *fc_msg;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600356 struct sfe_ipv4_create *p_sic;
357 struct sfe_connection *conn;
358 unsigned long flags;
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600359
Matthew McClintockd2854b72014-01-10 18:01:58 -0600360 na = info->attrs[FAST_CLASSIFIER_A_TUPLE];
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600361 fc_msg = nla_data(na);
Matthew McClintockea00adf2013-11-25 19:24:30 -0600362
Matthew McClintockd180a7d2014-01-17 11:21:35 -0600363 DEBUG_TRACE("INFO: want to offload: %d, %pI4, %pI4, %d, %d SMAC=%pM DMAC=%pM\n",
Matthew McClintockcf157352014-01-10 16:35:40 -0600364 fc_msg->proto,
365 &(fc_msg->src_saddr),
366 &(fc_msg->dst_saddr),
367 fc_msg->sport, fc_msg->dport,
Matthew McClintockd180a7d2014-01-17 11:21:35 -0600368 fc_msg->smac,
369 fc_msg->dmac);
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600370
Matthew McClintockea00adf2013-11-25 19:24:30 -0600371 spin_lock_irqsave(&sfe_connections_lock, flags);
372 list_for_each_entry(conn, &sfe_connections, list) {
Matthew McClintockea00adf2013-11-25 19:24:30 -0600373 p_sic = conn->sic;
374
375 DEBUG_TRACE(" -> COMPARING: proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d...",
376 p_sic->protocol, p_sic->src_ip, p_sic->dest_ip,
377 p_sic->src_port, p_sic->dest_port);
378
379 if (p_sic->protocol == fc_msg->proto &&
380 p_sic->src_port == fc_msg->sport &&
381 p_sic->dest_port == fc_msg->dport &&
382 p_sic->src_ip == fc_msg->src_saddr &&
383 p_sic->dest_ip == fc_msg->dst_saddr ) {
Matthew McClintock55c86982013-12-02 14:24:24 -0600384 if (conn->offloaded == 0) {
385 DEBUG_TRACE("USERSPACE OFFLOAD REQUEST, MATCH FOUND, WILL OFFLOAD\n");
386 if (fast_classifier_update_protocol(p_sic, conn->ct) == 0) {
387 spin_unlock_irqrestore(&sfe_connections_lock, flags);
388 DEBUG_TRACE("UNKNOWN PROTOCOL OR CONNECTION CLOSING, SKIPPING\n");
389 return 0;
390 }
391 DEBUG_TRACE("INFO: calling sfe rule creation!\n");
Matthew McClintockc5739382013-12-02 14:17:46 -0600392 spin_unlock_irqrestore(&sfe_connections_lock, flags);
Nicolas Costa514fde02014-01-13 15:50:29 -0600393 ret = sfe_ipv4_create_rule(p_sic);
394 if ((ret == 0) || (ret == -EADDRINUSE)) {
395 conn->offloaded = 1;
396 fast_classifier_send_genl_msg(FAST_CLASSIFIER_C_OFFLOADED, fc_msg);
397 }
Matthew McClintock75e75fb2014-01-17 14:26:17 -0600398 atomic_inc(&offload_msgs);
Matthew McClintockc5739382013-12-02 14:17:46 -0600399 return 0;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600400 }
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600401 /* conn->offloaded != 0 */
Matthew McClintock55c86982013-12-02 14:24:24 -0600402 DEBUG_TRACE("GOT REQUEST TO OFFLOAD ALREADY OFFLOADED CONN FROM USERSPACE\n");
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600403 spin_unlock_irqrestore(&sfe_connections_lock, flags);
404 return 0;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600405 }
406 DEBUG_TRACE("SEARCH CONTINUES\n");
407 }
408
409 spin_unlock_irqrestore(&sfe_connections_lock, flags);
Matthew McClintock75e75fb2014-01-17 14:26:17 -0600410 atomic_inc(&offload_no_match_msgs);
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600411 return 0;
412}
413
Matthew McClintock595ee8b2013-12-02 16:21:49 -0600414/* auto offload connection once we have this many packets*/
415static int offload_at_pkts = 128;
416
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600417/*
418 * fast_classifier_ipv4_post_routing_hook()
419 * Called for packets about to leave the box - either locally generated or forwarded from another interface
420 */
421static unsigned int fast_classifier_ipv4_post_routing_hook(unsigned int hooknum,
422 struct sk_buff *skb,
423 const struct net_device *in_unused,
424 const struct net_device *out,
425 int (*okfn)(struct sk_buff *))
426{
Nicolas Costa514fde02014-01-13 15:50:29 -0600427 int ret;
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600428 struct sfe_ipv4_create sic;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600429 struct sfe_ipv4_create *p_sic;
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600430 struct net_device *in;
431 struct nf_conn *ct;
432 enum ip_conntrack_info ctinfo;
433 struct net_device *src_dev;
434 struct net_device *dest_dev;
435 struct net_device *src_br_dev = NULL;
436 struct net_device *dest_br_dev = NULL;
437 struct nf_conntrack_tuple orig_tuple;
438 struct nf_conntrack_tuple reply_tuple;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600439 struct sfe_connection *conn;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600440 unsigned long flags;
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600441
442 /*
443 * Don't process broadcast or multicast packets.
444 */
445 if (unlikely(skb->pkt_type == PACKET_BROADCAST)) {
446 DEBUG_TRACE("broadcast, ignoring\n");
447 return NF_ACCEPT;
448 }
449 if (unlikely(skb->pkt_type == PACKET_MULTICAST)) {
450 DEBUG_TRACE("multicast, ignoring\n");
451 return NF_ACCEPT;
452 }
453
454 /*
455 * Don't process packets that are not being forwarded.
456 */
457 in = dev_get_by_index(&init_net, skb->skb_iif);
458 if (!in) {
459 DEBUG_TRACE("packet not forwarding\n");
460 return NF_ACCEPT;
461 }
462
463 /*
464 * Don't process packets with non-standard 802.3 MAC address sizes.
465 */
466 if (unlikely(in->addr_len != ETH_ALEN)) {
467 DEBUG_TRACE("in device: %s not 802.3 hw addr len: %u, ignoring\n",
468 in->name, (unsigned)in->addr_len);
469 goto done1;
470 }
471 if (unlikely(out->addr_len != ETH_ALEN)) {
472 DEBUG_TRACE("out device: %s not 802.3 hw addr len: %u, ignoring\n",
473 out->name, (unsigned)out->addr_len);
474 goto done1;
475 }
476
477 /*
478 * Don't process packets that aren't being tracked by conntrack.
479 */
480 ct = nf_ct_get(skb, &ctinfo);
481 if (unlikely(!ct)) {
482 DEBUG_TRACE("no conntrack connection, ignoring\n");
483 goto done1;
484 }
485
486 /*
487 * Don't process untracked connections.
488 */
489 if (unlikely(ct == &nf_conntrack_untracked)) {
490 DEBUG_TRACE("untracked connection\n");
491 goto done1;
492 }
493
494 /*
495 * Don't process connections that require support from a 'helper' (typically a NAT ALG).
496 */
497 if (unlikely(nfct_help(ct))) {
498 DEBUG_TRACE("connection has helper\n");
499 goto done1;
500 }
501
502 /*
503 * Look up the details of our connection in conntrack.
504 *
505 * Note that the data we get from conntrack is for the "ORIGINAL" direction
506 * but our packet may actually be in the "REPLY" direction.
507 */
508 orig_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
509 reply_tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
510 sic.protocol = (int32_t)orig_tuple.dst.protonum;
511
512 /*
513 * Get addressing information, non-NAT first
514 */
515 sic.src_ip = (__be32)orig_tuple.src.u3.ip;
516 sic.dest_ip = (__be32)orig_tuple.dst.u3.ip;
517
518 /*
519 * NAT'ed addresses - note these are as seen from the 'reply' direction
520 * When NAT does not apply to this connection these will be identical to the above.
521 */
522 sic.src_ip_xlate = (__be32)reply_tuple.dst.u3.ip;
523 sic.dest_ip_xlate = (__be32)reply_tuple.src.u3.ip;
524
525 sic.flags = 0;
526
527 switch (sic.protocol) {
528 case IPPROTO_TCP:
529 sic.src_port = orig_tuple.src.u.tcp.port;
530 sic.dest_port = orig_tuple.dst.u.tcp.port;
531 sic.src_port_xlate = reply_tuple.dst.u.tcp.port;
532 sic.dest_port_xlate = reply_tuple.src.u.tcp.port;
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600533
534 /*
535 * Don't try to manage a non-established connection.
536 */
537 if (!test_bit(IPS_ASSURED_BIT, &ct->status)) {
538 DEBUG_TRACE("non-established connection\n");
539 goto done1;
540 }
541
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600542 break;
543
544 case IPPROTO_UDP:
545 sic.src_port = orig_tuple.src.u.udp.port;
546 sic.dest_port = orig_tuple.dst.u.udp.port;
547 sic.src_port_xlate = reply_tuple.dst.u.udp.port;
548 sic.dest_port_xlate = reply_tuple.src.u.udp.port;
549 break;
550
551 default:
552 DEBUG_TRACE("unhandled protocol %d\n", sic.protocol);
553 goto done1;
554 }
555
556 /*
Matthew McClintockea00adf2013-11-25 19:24:30 -0600557 * If we already have this connection in our list, skip it
558 * XXX: this may need to be optimized
559 */
560 DEBUG_TRACE("POST_ROUTE: checking new connection: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d\n",
561 sic.protocol, sic.src_ip, sic.dest_ip,
562 sic.src_port, sic.dest_port);
563 spin_lock_irqsave(&sfe_connections_lock, flags);
564 list_for_each_entry(conn, &sfe_connections, list) {
565 p_sic = conn->sic;
566 DEBUG_TRACE("\t\t-> COMPARING: proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d...",
567 p_sic->protocol, p_sic->src_ip, p_sic->dest_ip,
568 p_sic->src_port, p_sic->dest_port);
569
570 if (p_sic->protocol == sic.protocol &&
571 p_sic->src_port == sic.src_port &&
572 p_sic->dest_port == sic.dest_port &&
573 p_sic->src_ip == sic.src_ip &&
574 p_sic->dest_ip == sic.dest_ip ) {
Matthew McClintockc5739382013-12-02 14:17:46 -0600575 conn->hits++;
Matthew McClintock55c86982013-12-02 14:24:24 -0600576 if (conn->offloaded == 0) {
Matthew McClintock595ee8b2013-12-02 16:21:49 -0600577 if (conn->hits == offload_at_pkts) {
Nicolas Costa514fde02014-01-13 15:50:29 -0600578 struct fast_classifier_tuple fc_msg;
Matthew McClintock55c86982013-12-02 14:24:24 -0600579 DEBUG_TRACE("OFFLOADING CONNECTION, TOO MANY HITS\n");
580 if (fast_classifier_update_protocol(p_sic, conn->ct) == 0) {
581 spin_unlock_irqrestore(&sfe_connections_lock, flags);
582 DEBUG_TRACE("UNKNOWN PROTOCOL OR CONNECTION CLOSING, SKIPPING\n");
583 return 0;
584 }
585 DEBUG_TRACE("INFO: calling sfe rule creation!\n");
Matthew McClintockc5739382013-12-02 14:17:46 -0600586 spin_unlock_irqrestore(&sfe_connections_lock, flags);
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600587
Nicolas Costa514fde02014-01-13 15:50:29 -0600588 ret = sfe_ipv4_create_rule(p_sic);
589 if ((ret == 0) || (ret == -EADDRINUSE)) {
590 conn->offloaded = 1;
591 fc_msg.proto = sic.protocol;
592 fc_msg.src_saddr = sic.src_ip;
593 fc_msg.dst_saddr = sic.dest_ip;
594 fc_msg.sport = sic.src_port;
595 fc_msg.dport = sic.dest_port;
Matthew McClintockd180a7d2014-01-17 11:21:35 -0600596 memcpy(fc_msg.smac, conn->smac, ETH_ALEN);
597 memcpy(fc_msg.dmac, conn->dmac, ETH_ALEN);
Nicolas Costa514fde02014-01-13 15:50:29 -0600598 fast_classifier_send_genl_msg(FAST_CLASSIFIER_C_OFFLOADED, &fc_msg);
599 }
600
Matthew McClintock16a47ec2013-12-05 17:03:15 -0600601 goto done1;
Matthew McClintock595ee8b2013-12-02 16:21:49 -0600602 } else if (conn->hits > offload_at_pkts) {
603 DEBUG_ERROR("ERROR: MORE THAN %d HITS AND NOT OFFLOADED\n", offload_at_pkts);
Matthew McClintock16a47ec2013-12-05 17:03:15 -0600604 spin_unlock_irqrestore(&sfe_connections_lock, flags);
605 goto done1;
Matthew McClintockc5739382013-12-02 14:17:46 -0600606 }
Matthew McClintock16a47ec2013-12-05 17:03:15 -0600607 }
608
Nicolas Costa514fde02014-01-13 15:50:29 -0600609 spin_unlock_irqrestore(&sfe_connections_lock, flags);
Matthew McClintock16a47ec2013-12-05 17:03:15 -0600610 if (conn->offloaded == 1) {
Nicolas Costa514fde02014-01-13 15:50:29 -0600611 sfe_ipv4_update_rule(p_sic);
Matthew McClintockc5739382013-12-02 14:17:46 -0600612 }
613
614 DEBUG_TRACE("FOUND, SKIPPING\n");
Matthew McClintockea00adf2013-11-25 19:24:30 -0600615 goto done1;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600616 }
617
Matthew McClintock55c86982013-12-02 14:24:24 -0600618 DEBUG_TRACE("SEARCH CONTINUES");
Matthew McClintockea00adf2013-11-25 19:24:30 -0600619 }
620 spin_unlock_irqrestore(&sfe_connections_lock, flags);
621
622 /*
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600623 * Get the MAC addresses that correspond to source and destination host addresses.
624 */
625 if (!fast_classifier_find_mac_addr(sic.src_ip, sic.src_mac)) {
626 DEBUG_TRACE("failed to find MAC address for src IP: %pI4\n", &sic.src_ip);
627 goto done1;
628 }
629
630 if (!fast_classifier_find_mac_addr(sic.src_ip_xlate, sic.src_mac_xlate)) {
631 DEBUG_TRACE("failed to find MAC address for xlate src IP: %pI4\n", &sic.src_ip_xlate);
632 goto done1;
633 }
634
635 /*
636 * Do dest now
637 */
638 if (!fast_classifier_find_mac_addr(sic.dest_ip, sic.dest_mac)) {
639 DEBUG_TRACE("failed to find MAC address for dest IP: %pI4\n", &sic.dest_ip);
640 goto done1;
641 }
642
643 if (!fast_classifier_find_mac_addr(sic.dest_ip_xlate, sic.dest_mac_xlate)) {
644 DEBUG_TRACE("failed to find MAC address for xlate dest IP: %pI4\n", &sic.dest_ip_xlate);
645 goto done1;
646 }
647
648 /*
649 * Get our device info. If we're dealing with the "reply" direction here then
650 * we'll need things swapped around.
651 */
652 if (ctinfo < IP_CT_IS_REPLY) {
653 src_dev = in;
654 dest_dev = (struct net_device *)out;
655 } else {
656 src_dev = (struct net_device *)out;
657 dest_dev = in;
658 }
659
660#if (!SFE_HOOK_ABOVE_BRIDGE)
661 /*
662 * Now our devices may actually be a bridge interface. If that's
663 * the case then we need to hunt down the underlying interface.
664 */
665 if (src_dev->priv_flags & IFF_EBRIDGE) {
666 src_br_dev = br_port_dev_get(src_dev, sic.src_mac);
667 if (!src_br_dev) {
668 DEBUG_TRACE("no port found on bridge\n");
669 goto done1;
670 }
671
672 src_dev = src_br_dev;
673 }
674
675 if (dest_dev->priv_flags & IFF_EBRIDGE) {
676 dest_br_dev = br_port_dev_get(dest_dev, sic.dest_mac_xlate);
677 if (!dest_br_dev) {
678 DEBUG_TRACE("no port found on bridge\n");
679 goto done2;
680 }
681
682 dest_dev = dest_br_dev;
683 }
684#else
685 /*
686 * Our devices may actually be part of a bridge interface. If that's
687 * the case then find the bridge interface instead.
688 */
689 if (src_dev->priv_flags & IFF_BRIDGE_PORT) {
690 src_br_dev = src_dev->master;
691 if (!src_br_dev) {
692 DEBUG_TRACE("no bridge found for: %s\n", src_dev->name);
693 goto done1;
694 }
695
696 dev_hold(src_br_dev);
697 src_dev = src_br_dev;
698 }
699
700 if (dest_dev->priv_flags & IFF_BRIDGE_PORT) {
701 dest_br_dev = dest_dev->master;
702 if (!dest_br_dev) {
703 DEBUG_TRACE("no bridge found for: %s\n", dest_dev->name);
704 goto done2;
705 }
706
707 dev_hold(dest_br_dev);
708 dest_dev = dest_br_dev;
709 }
710#endif
711
712 sic.src_dev = src_dev;
713 sic.dest_dev = dest_dev;
714
715// XXX - these MTUs need handling correctly!
716 sic.src_mtu = 1500;
717 sic.dest_mtu = 1500;
718
Matthew McClintocke1cf6f22013-11-27 13:27:09 -0600719 if (skb->mark) {
720 DEBUG_TRACE("SKB MARK NON ZERO %x\n", skb->mark);
721 }
722 sic.mark = skb->mark;
723
Matthew McClintockea00adf2013-11-25 19:24:30 -0600724 conn = kmalloc(sizeof(struct sfe_connection), GFP_KERNEL);
725 if (conn == NULL) {
726 printk(KERN_CRIT "ERROR: no memory for sfe\n");
727 goto done3;
728 }
Matthew McClintockc5739382013-12-02 14:17:46 -0600729 conn->hits = 0;
Matthew McClintock55c86982013-12-02 14:24:24 -0600730 conn->offloaded = 0;
Matthew McClintockcf157352014-01-10 16:35:40 -0600731 DEBUG_TRACE("Source MAC=%pM\n", mh->h_source);
Matthew McClintockab00ff22014-01-17 16:23:34 -0600732 memcpy(conn->smac, sic.src_mac, ETH_ALEN);
733 memcpy(conn->dmac, sic.dest_mac_xlate, ETH_ALEN);
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600734
Matthew McClintockea00adf2013-11-25 19:24:30 -0600735 p_sic = kmalloc(sizeof(struct sfe_ipv4_create), GFP_KERNEL);
736 if (p_sic == NULL) {
737 printk(KERN_CRIT "ERROR: no memory for sfe\n");
738 kfree(conn);
739 goto done3;
740 }
741
742 memcpy(p_sic, &sic, sizeof(sic));
743 conn->sic = p_sic;
744 conn->ct = ct;
Matthew McClintock75e75fb2014-01-17 14:26:17 -0600745 sfe_connections_size++;
746 DEBUG_TRACE(" -> adding item to sfe_connections, new size: %d\n", sfe_connections_size);
Matthew McClintockea00adf2013-11-25 19:24:30 -0600747 DEBUG_TRACE("POST_ROUTE: new offloadable connection: proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d\n",
748 p_sic->protocol, p_sic->src_ip, p_sic->dest_ip,
749 p_sic->src_port, p_sic->dest_port);
750 spin_lock_irqsave(&sfe_connections_lock, flags);
751 list_add_tail(&(conn->list), &sfe_connections);
752 spin_unlock_irqrestore(&sfe_connections_lock, flags);
753done3:
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600754 /*
755 * If we had bridge ports then release them too.
756 */
757 if (dest_br_dev) {
758 dev_put(dest_br_dev);
759 }
760
761done2:
762 if (src_br_dev) {
763 dev_put(src_br_dev);
764 }
765
766done1:
767 /*
768 * Release the interface on which this skb arrived
769 */
770 dev_put(in);
771
772 return NF_ACCEPT;
773}
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600774
Nicolas Costa21090402014-01-15 11:47:10 -0600775/*
776 * fast_classifier_update_mark()
777 * updates the mark for a fast-classifier connection
778 */
779static void fast_classifier_update_mark(struct sfe_ipv4_mark *mark)
780{
781 struct sfe_connection *conn;
782 struct sfe_ipv4_create *p_sic;
783 unsigned long flags;
784
785 spin_lock_irqsave(&sfe_connections_lock, flags);
786 list_for_each_entry(conn, &sfe_connections, list) {
787 p_sic = conn->sic;
788 if (p_sic->protocol == mark->protocol &&
789 p_sic->src_port == mark->src_port &&
790 p_sic->dest_port == mark->dest_port &&
791 p_sic->src_ip == mark->src_ip &&
792 p_sic->dest_ip == mark->dest_ip ) {
793
794 p_sic->mark = mark->mark;
795
796 break;
797 }
798 }
799 spin_unlock_irqrestore(&sfe_connections_lock, flags);
800}
801
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600802#ifdef CONFIG_NF_CONNTRACK_EVENTS
803/*
804 * fast_classifier_conntrack_event()
805 * Callback event invoked when a conntrack connection's state changes.
806 */
Matthew McClintock0680e9f2013-11-26 15:43:10 -0600807#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
808static int fast_classifier_conntrack_event(struct notifier_block *this,
809 unsigned int events, struct nf_ct_event *item)
810#else
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600811static int fast_classifier_conntrack_event(unsigned int events, struct nf_ct_event *item)
Matthew McClintock0680e9f2013-11-26 15:43:10 -0600812#endif
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600813{
814 struct sfe_ipv4_destroy sid;
815 struct nf_conn *ct = item->ct;
816 struct nf_conntrack_tuple orig_tuple;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600817 struct sfe_connection *conn;
818 struct sfe_ipv4_create *p_sic;
819 int sfe_found_match = 0;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600820 unsigned long flags;
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600821 struct fast_classifier_tuple fc_msg;
Matthew McClintock5f1b1d42014-01-17 14:43:00 -0600822 int offloaded = 0;
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600823
824 /*
825 * If we don't have a conntrack entry then we're done.
826 */
827 if (unlikely(!ct)) {
828 DEBUG_WARN("no ct in conntrack event callback\n");
829 return NOTIFY_DONE;
830 }
831
832 /*
833 * If this is an untracked connection then we can't have any state either.
834 */
835 if (unlikely(ct == &nf_conntrack_untracked)) {
836 DEBUG_TRACE("ignoring untracked conn\n");
837 return NOTIFY_DONE;
838 }
839
840 /*
841 * Ignore anything other than IPv4 connections.
842 */
843 if (unlikely(nf_ct_l3num(ct) != AF_INET)) {
844 DEBUG_TRACE("ignoring non-IPv4 conn\n");
845 return NOTIFY_DONE;
846 }
847
848 /*
Nicolas Costab5f377c2014-01-13 16:16:13 -0600849 * Check for an updated mark
850 */
851 if ((events & (1 << IPCT_MARK)) && (ct->mark != 0)) {
852 struct sfe_ipv4_mark mark;
853 orig_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
854
855 mark.protocol = (int32_t)orig_tuple.dst.protonum;
856 mark.src_ip = (__be32)orig_tuple.src.u3.ip;
857 mark.dest_ip = (__be32)orig_tuple.dst.u3.ip;
858 switch (mark.protocol) {
859 case IPPROTO_TCP:
860 mark.src_port = orig_tuple.src.u.tcp.port;
861 mark.dest_port = orig_tuple.dst.u.tcp.port;
862 break;
863 case IPPROTO_UDP:
864 mark.src_port = orig_tuple.src.u.udp.port;
865 mark.dest_port = orig_tuple.dst.u.udp.port;
866 break;
867 default:
868 break;
869 }
870
871 mark.mark = ct->mark;
872 sfe_ipv4_mark_rule(&mark);
Nicolas Costa21090402014-01-15 11:47:10 -0600873 fast_classifier_update_mark(&mark);
Nicolas Costab5f377c2014-01-13 16:16:13 -0600874 }
875
876 /*
Matthew McClintocke1cf6f22013-11-27 13:27:09 -0600877 * We're only interested in destroy events at this point
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600878 */
879 if (unlikely(!(events & (1 << IPCT_DESTROY)))) {
880 DEBUG_TRACE("ignoring non-destroy event\n");
881 return NOTIFY_DONE;
882 }
883
884 orig_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
885 sid.protocol = (int32_t)orig_tuple.dst.protonum;
886
887 /*
888 * Extract information from the conntrack connection. We're only interested
889 * in nominal connection information (i.e. we're ignoring any NAT information).
890 */
891 sid.src_ip = (__be32)orig_tuple.src.u3.ip;
892 sid.dest_ip = (__be32)orig_tuple.dst.u3.ip;
893
894 switch (sid.protocol) {
895 case IPPROTO_TCP:
896 sid.src_port = orig_tuple.src.u.tcp.port;
897 sid.dest_port = orig_tuple.dst.u.tcp.port;
898 break;
899
900 case IPPROTO_UDP:
901 sid.src_port = orig_tuple.src.u.udp.port;
902 sid.dest_port = orig_tuple.dst.u.udp.port;
903 break;
904
905 default:
906 DEBUG_TRACE("unhandled protocol: %d\n", sid.protocol);
907 return NOTIFY_DONE;
908 }
909
Matthew McClintockea00adf2013-11-25 19:24:30 -0600910 /*
911 * If we already have this connection in our list, skip it
912 * XXX: this may need to be optimized
913 */
914 DEBUG_TRACE("INFO: want to clean up: proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d\n",
915 sid.protocol, sid.src_ip, sid.dest_ip,
916 sid.src_port, sid.dest_port);
917 spin_lock_irqsave(&sfe_connections_lock, flags);
918 list_for_each_entry(conn, &sfe_connections, list) {
919 p_sic = conn->sic;
920 DEBUG_TRACE(" -> COMPARING: proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d...",
921 p_sic->protocol, p_sic->src_ip, p_sic->dest_ip,
922 p_sic->src_port, p_sic->dest_port);
923
924 if (p_sic->protocol == sid.protocol &&
925 p_sic->src_port == sid.src_port &&
926 p_sic->dest_port == sid.dest_port &&
927 p_sic->src_ip == sid.src_ip &&
928 p_sic->dest_ip == sid.dest_ip ) {
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600929 fc_msg.proto = p_sic->protocol;
930 fc_msg.src_saddr = p_sic->src_ip;
931 fc_msg.dst_saddr = p_sic->dest_ip;
932 fc_msg.sport = p_sic->src_port;
933 fc_msg.dport = p_sic->dest_port;
Matthew McClintockd180a7d2014-01-17 11:21:35 -0600934 memcpy(fc_msg.smac, conn->smac, ETH_ALEN);
935 memcpy(fc_msg.dmac, conn->dmac, ETH_ALEN);
Matthew McClintockea00adf2013-11-25 19:24:30 -0600936 sfe_found_match = 1;
Matthew McClintock5f1b1d42014-01-17 14:43:00 -0600937 offloaded = conn->offloaded;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600938 DEBUG_TRACE("FOUND, DELETING\n");
939 break;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600940 }
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600941 DEBUG_TRACE("SEARCH CONTINUES\n");
Matthew McClintockea00adf2013-11-25 19:24:30 -0600942 }
943
944 if (sfe_found_match) {
945 DEBUG_TRACE("INFO: connection over proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d\n",
946 p_sic->protocol, p_sic->src_ip, p_sic->dest_ip,
947 p_sic->src_port, p_sic->dest_port);
948 kfree(conn->sic);
949 list_del(&(conn->list));
Matthew McClintock75e75fb2014-01-17 14:26:17 -0600950 sfe_connections_size--;
Matthew McClintockea00adf2013-11-25 19:24:30 -0600951 kfree(conn);
952 } else {
953 DEBUG_TRACE("NO MATCH FOUND IN %d ENTRIES!!\n", sfe_connections_size);
954 }
955 spin_unlock_irqrestore(&sfe_connections_lock, flags);
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600956
957 sfe_ipv4_destroy_rule(&sid);
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600958
Matthew McClintock5f1b1d42014-01-17 14:43:00 -0600959 if (sfe_found_match && offloaded) {
Matthew McClintocke4f9a672014-01-06 17:04:04 -0600960 fast_classifier_send_genl_msg(FAST_CLASSIFIER_C_DONE, &fc_msg);
961 }
962
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600963 return NOTIFY_DONE;
964}
965
966/*
967 * Netfilter conntrack event system to monitor connection tracking changes
968 */
Matthew McClintock0680e9f2013-11-26 15:43:10 -0600969#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
970static struct notifier_block fast_classifier_conntrack_notifier = {
971 .notifier_call = fast_classifier_conntrack_event,
972};
973#else
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600974static struct nf_ct_event_notifier fast_classifier_conntrack_notifier = {
975 .fcn = fast_classifier_conntrack_event,
976};
977#endif
Matthew McClintock0680e9f2013-11-26 15:43:10 -0600978#endif
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600979
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600980/*
981 * Structure to establish a hook into the post routing netfilter point - this
982 * will pick up local outbound and packets going from one interface to another.
983 *
984 * Note: see include/linux/netfilter_ipv4.h for info related to priority levels.
985 * We want to examine packets after NAT translation and any ALG processing.
986 */
987static struct nf_hook_ops fast_classifier_ipv4_ops_post_routing[] __read_mostly = {
988 {
989 .hook = fast_classifier_ipv4_post_routing_hook,
990 .owner = THIS_MODULE,
991 .pf = PF_INET,
992 .hooknum = NF_INET_POST_ROUTING,
993 .priority = NF_IP_PRI_NAT_SRC + 1,
994 },
995};
Matthew McClintocke1bcfe42013-11-22 15:33:09 -0600996
997/*
998 * fast_classifier_sync_rule()
999 * Synchronize a connection's state.
1000 */
1001static void fast_classifier_sync_rule(struct sfe_ipv4_sync *sis)
1002{
1003 struct nf_conntrack_tuple_hash *h;
1004 struct nf_conntrack_tuple tuple;
1005 struct nf_conn *ct;
1006 struct nf_conn_counter *acct;
1007
1008 /*
1009 * Create a tuple so as to be able to look up a connection
1010 */
1011 memset(&tuple, 0, sizeof(tuple));
1012 tuple.src.u3.ip = sis->src_ip;
1013 tuple.src.u.all = (__be16)sis->src_port;
1014 tuple.src.l3num = AF_INET;
1015
1016 tuple.dst.u3.ip = sis->dest_ip;
1017 tuple.dst.dir = IP_CT_DIR_ORIGINAL;
1018 tuple.dst.protonum = (uint8_t)sis->protocol;
1019 tuple.dst.u.all = (__be16)sis->dest_port;
1020
1021 DEBUG_TRACE("update connection - p: %d, s: %pI4:%u, d: %pI4:%u\n",
1022 (int)tuple.dst.protonum,
1023 &tuple.src.u3.ip, (unsigned int)ntohs(tuple.src.u.all),
1024 &tuple.dst.u3.ip, (unsigned int)ntohs(tuple.dst.u.all));
1025
1026 /*
1027 * Look up conntrack connection
1028 */
1029 h = nf_conntrack_find_get(&init_net, NF_CT_DEFAULT_ZONE, &tuple);
1030 if (unlikely(!h)) {
1031 DEBUG_TRACE("no connection found\n");
1032 return;
1033 }
1034
1035 ct = nf_ct_tuplehash_to_ctrack(h);
1036 NF_CT_ASSERT(ct->timeout.data == (unsigned long)ct);
1037
1038 /*
1039 * Only update if this is not a fixed timeout
1040 */
1041 if (!test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
1042 ct->timeout.expires += sis->delta_jiffies;
1043 }
1044
1045 acct = nf_conn_acct_find(ct);
1046 if (acct) {
1047 spin_lock_bh(&ct->lock);
Matthew McClintock704b7a62013-12-19 16:13:01 -06001048 atomic64_set(&acct[IP_CT_DIR_ORIGINAL].packets, sis->src_packet_count);
1049 atomic64_set(&acct[IP_CT_DIR_ORIGINAL].bytes, sis->src_byte_count);
1050 atomic64_set(&acct[IP_CT_DIR_REPLY].packets, sis->dest_packet_count);
1051 atomic64_set(&acct[IP_CT_DIR_REPLY].bytes, sis->dest_byte_count);
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001052 spin_unlock_bh(&ct->lock);
1053 }
1054
1055 switch (sis->protocol) {
1056 case IPPROTO_TCP:
1057 spin_lock_bh(&ct->lock);
1058 if (ct->proto.tcp.seen[0].td_maxwin < sis->src_td_max_window) {
1059 ct->proto.tcp.seen[0].td_maxwin = sis->src_td_max_window;
1060 }
1061 if ((int32_t)(ct->proto.tcp.seen[0].td_end - sis->src_td_end) < 0) {
1062 ct->proto.tcp.seen[0].td_end = sis->src_td_end;
1063 }
1064 if ((int32_t)(ct->proto.tcp.seen[0].td_maxend - sis->src_td_max_end) < 0) {
1065 ct->proto.tcp.seen[0].td_maxend = sis->src_td_max_end;
1066 }
1067 if (ct->proto.tcp.seen[1].td_maxwin < sis->dest_td_max_window) {
1068 ct->proto.tcp.seen[1].td_maxwin = sis->dest_td_max_window;
1069 }
1070 if ((int32_t)(ct->proto.tcp.seen[1].td_end - sis->dest_td_end) < 0) {
1071 ct->proto.tcp.seen[1].td_end = sis->dest_td_end;
1072 }
1073 if ((int32_t)(ct->proto.tcp.seen[1].td_maxend - sis->dest_td_max_end) < 0) {
1074 ct->proto.tcp.seen[1].td_maxend = sis->dest_td_max_end;
1075 }
1076 spin_unlock_bh(&ct->lock);
1077 break;
1078 }
1079
1080 /*
1081 * Release connection
1082 */
1083 nf_ct_put(ct);
1084}
1085
1086/*
1087 * fast_classifier_device_event()
1088 */
1089static int fast_classifier_device_event(struct notifier_block *this, unsigned long event, void *ptr)
1090{
1091 struct net_device *dev = (struct net_device *)ptr;
1092
1093 switch (event) {
1094 case NETDEV_DOWN:
1095 if (dev) {
1096 sfe_ipv4_destroy_all_rules_for_dev(dev);
1097 }
1098 break;
1099 }
1100
1101 return NOTIFY_DONE;
1102}
1103
1104/*
1105 * fast_classifier_inet_event()
1106 */
1107static int fast_classifier_inet_event(struct notifier_block *this, unsigned long event, void *ptr)
1108{
1109 struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev;
1110 return fast_classifier_device_event(this, event, dev);
1111}
1112
1113/*
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001114 * fast_classifier_get_offload_at_pkts()
1115 */
1116static ssize_t fast_classifier_get_offload_at_pkts(struct device *dev,
1117 struct device_attribute *attr,
1118 char *buf)
1119{
1120 return sprintf(buf, "%d\n", offload_at_pkts);
1121}
1122
1123/*
1124 * fast_classifier_set_offload_at_pkts()
1125 */
1126static ssize_t fast_classifier_set_offload_at_pkts(struct device *dev,
1127 struct device_attribute *attr,
1128 char *buf, size_t size)
1129{
Matthew McClintock8f4fcd22014-01-14 21:06:03 -06001130 long new;
1131 int ret;
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001132
Matthew McClintock8f4fcd22014-01-14 21:06:03 -06001133 printk(KERN_EMERG "BUF: %s\n", buf);
1134 ret = strict_strtol(buf, 0, &new);
1135 if (ret == -EINVAL || ((int)new != new))
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001136 return -EINVAL;
1137
1138 offload_at_pkts = new;
1139
1140 return size;
1141}
1142
1143/*
Matthew McClintock75e75fb2014-01-17 14:26:17 -06001144 * fast_classifier_get_debug_info()
1145 */
1146static ssize_t fast_classifier_get_debug_info(struct device *dev,
1147 struct device_attribute *attr,
1148 char *buf)
1149{
1150 size_t len = 0;
1151 unsigned long flags;
1152 struct sfe_connection *conn;
1153
1154 spin_lock_irqsave(&sfe_connections_lock, flags);
1155 len += scnprintf(buf, PAGE_SIZE - len, "len = %d msg sent: offload = %d offload_no_match = %d"
1156 " offloaded = %d done = %d\n",
1157 sfe_connections_size,
1158 atomic_read(&offload_msgs),
1159 atomic_read(&offload_no_match_msgs),
1160 atomic_read(&offloaded_msgs),
1161 atomic_read(&done_msgs));
1162 list_for_each_entry(conn, &sfe_connections, list) {
1163 len += scnprintf(buf + len , PAGE_SIZE - len, "offloaded=%d, proto=%d, src_ip=%pI4, dest_ip=%pI4,"
1164 " src_port=%d, dest_port=%d SMAC=%pM DMAC=%pM mark=%08x\n",
1165 conn->offloaded,
1166 conn->sic->protocol,
1167 &(conn->sic->src_ip),
1168 &(conn->sic->dest_ip),
1169 conn->sic->src_port,
1170 conn->sic->dest_port,
1171 conn->smac,
1172 conn->dmac,
1173 conn->sic->mark);
1174 }
1175 spin_unlock_irqrestore(&sfe_connections_lock, flags);
1176
1177 return len;
1178}
1179
1180/*
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001181 * sysfs attributes.
1182 */
1183static const struct device_attribute fast_classifier_offload_at_pkts_attr =
1184 __ATTR(offload_at_pkts, S_IWUGO | S_IRUGO, fast_classifier_get_offload_at_pkts, fast_classifier_set_offload_at_pkts);
Matthew McClintock75e75fb2014-01-17 14:26:17 -06001185static const struct device_attribute fast_classifier_debug_info_attr =
1186 __ATTR(debug_info, S_IRUGO, fast_classifier_get_debug_info, NULL);
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001187
1188/*
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001189 * fast_classifier_init()
1190 */
1191static int __init fast_classifier_init(void)
1192{
1193 struct fast_classifier *sc = &__sc;
1194 int result = -1;
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -06001195
1196 printk(KERN_ALERT "fast-classifier: starting up\n");
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001197 DEBUG_INFO("SFE CM init\n");
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -06001198
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001199 /*
1200 * Create sys/fast_classifier
1201 */
1202 sc->sys_fast_classifier = kobject_create_and_add("fast_classifier", NULL);
1203 if (!sc->sys_fast_classifier) {
1204 DEBUG_ERROR("failed to register fast_classifier\n");
1205 goto exit1;
1206 }
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -06001207
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001208 result = sysfs_create_file(sc->sys_fast_classifier, &fast_classifier_offload_at_pkts_attr.attr);
1209 if (result) {
1210 DEBUG_ERROR("failed to register debug dev file: %d\n", result);
1211 goto exit2;
1212 }
1213
Matthew McClintock75e75fb2014-01-17 14:26:17 -06001214 result = sysfs_create_file(sc->sys_fast_classifier, &fast_classifier_debug_info_attr.attr);
1215 if (result) {
1216 DEBUG_ERROR("failed to register debug dev file: %d\n", result);
1217 sysfs_remove_file(sc->sys_fast_classifier, &fast_classifier_offload_at_pkts_attr.attr);
1218 goto exit2;
1219 }
1220
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001221 sc->dev_notifier.notifier_call = fast_classifier_device_event;
1222 sc->dev_notifier.priority = 1;
1223 register_netdevice_notifier(&sc->dev_notifier);
1224
1225 sc->inet_notifier.notifier_call = fast_classifier_inet_event;
1226 sc->inet_notifier.priority = 1;
1227 register_inetaddr_notifier(&sc->inet_notifier);
1228
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001229 /*
1230 * Register our netfilter hooks.
1231 */
1232 result = nf_register_hooks(fast_classifier_ipv4_ops_post_routing, ARRAY_SIZE(fast_classifier_ipv4_ops_post_routing));
1233 if (result < 0) {
1234 DEBUG_ERROR("can't register nf post routing hook: %d\n", result);
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001235 goto exit3;
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001236 }
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001237
1238#ifdef CONFIG_NF_CONNTRACK_EVENTS
1239 /*
1240 * Register a notifier hook to get fast notifications of expired connections.
1241 */
1242 result = nf_conntrack_register_notifier(&init_net, &fast_classifier_conntrack_notifier);
1243 if (result < 0) {
1244 DEBUG_ERROR("can't register nf notifier hook: %d\n", result);
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001245 goto exit4;
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001246 }
1247#endif
1248
Dave Hudsonfd7fd072013-12-07 22:34:18 +00001249 result = genl_register_family(&fast_classifier_gnl_family);
Matthew McClintocke4f9a672014-01-06 17:04:04 -06001250 if (result != 0) {
1251 printk(KERN_CRIT "unable to register genl family\n");
Dave Hudsonfd7fd072013-12-07 22:34:18 +00001252 goto exit5;
1253 }
1254
Matthew McClintocke4f9a672014-01-06 17:04:04 -06001255 result = genl_register_ops(&fast_classifier_gnl_family, fast_classifier_gnl_ops);
Dave Hudsonfd7fd072013-12-07 22:34:18 +00001256 if (result != 0) {
Matthew McClintocke4f9a672014-01-06 17:04:04 -06001257 printk(KERN_CRIT "unable to register ops\n");
1258 goto exit6;
1259 }
1260
1261 result = genl_register_mc_group(&fast_classifier_gnl_family,
1262 &fast_classifier_genl_mcgrp);
1263 if (result != 0) {
1264 printk(KERN_CRIT "unable to register multicast group\n");
Dave Hudsonfd7fd072013-12-07 22:34:18 +00001265 goto exit6;
1266 }
1267
1268 printk(KERN_ALERT "fast-classifier: registered\n");
1269
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001270 spin_lock_init(&sc->lock);
1271
1272 /*
1273 * Hook the receive path in the network stack.
1274 */
1275 BUG_ON(athrs_fast_nat_recv != NULL);
1276 RCU_INIT_POINTER(athrs_fast_nat_recv, fast_classifier_recv);
1277
1278 /*
1279 * Hook the shortcut sync callback.
1280 */
1281 sfe_ipv4_register_sync_rule_callback(fast_classifier_sync_rule);
1282
Matthew McClintock6f29aa12013-11-06 15:49:01 -06001283 return 0;
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -06001284
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001285exit6:
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001286 genl_unregister_family(&fast_classifier_gnl_family);
1287
1288exit5:
1289#ifdef CONFIG_NF_CONNTRACK_EVENTS
Dave Hudsonfd7fd072013-12-07 22:34:18 +00001290 nf_conntrack_unregister_notifier(&init_net, &fast_classifier_conntrack_notifier);
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001291#endif
1292
1293exit4:
Dave Hudsonfd7fd072013-12-07 22:34:18 +00001294 nf_unregister_hooks(fast_classifier_ipv4_ops_post_routing, ARRAY_SIZE(fast_classifier_ipv4_ops_post_routing));
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001295
1296exit3:
Dave Hudsonfd7fd072013-12-07 22:34:18 +00001297 unregister_inetaddr_notifier(&sc->inet_notifier);
1298 unregister_netdevice_notifier(&sc->dev_notifier);
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001299 sysfs_remove_file(sc->sys_fast_classifier, &fast_classifier_offload_at_pkts_attr.attr);
Matthew McClintock75e75fb2014-01-17 14:26:17 -06001300 sysfs_remove_file(sc->sys_fast_classifier, &fast_classifier_debug_info_attr.attr);
Matthew McClintock595ee8b2013-12-02 16:21:49 -06001301
1302exit2:
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001303 kobject_put(sc->sys_fast_classifier);
1304
1305exit1:
1306 return result;
Matthew McClintock6f29aa12013-11-06 15:49:01 -06001307}
1308
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001309/*
1310 * fast_classifier_exit()
1311 */
1312static void __exit fast_classifier_exit(void)
Matthew McClintock6f29aa12013-11-06 15:49:01 -06001313{
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001314 struct fast_classifier *sc = &__sc;
1315 int result = -1;
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -06001316
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001317 DEBUG_INFO("SFE CM exit\n");
Matthew McClintock6ab3b3f2013-11-14 15:39:15 -06001318 printk(KERN_ALERT "fast-classifier: shutting down\n");
1319
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001320 /*
1321 * Unregister our sync callback.
1322 */
1323 sfe_ipv4_register_sync_rule_callback(NULL);
1324
1325 /*
1326 * Unregister our receive callback.
1327 */
1328 RCU_INIT_POINTER(athrs_fast_nat_recv, NULL);
1329
1330 /*
1331 * Wait for all callbacks to complete.
1332 */
1333 rcu_barrier();
1334
1335 /*
1336 * Destroy all connections.
1337 */
1338 sfe_ipv4_destroy_all_rules_for_dev(NULL);
1339
Matthew McClintocke4f9a672014-01-06 17:04:04 -06001340 result = genl_unregister_ops(&fast_classifier_gnl_family, fast_classifier_gnl_ops);
Dave Hudsonfd7fd072013-12-07 22:34:18 +00001341 if (result != 0) {
1342 printk(KERN_CRIT "Unable to unreigster genl_ops\n");
1343 }
1344
1345 result = genl_unregister_family(&fast_classifier_gnl_family);
1346 if (result != 0) {
1347 printk(KERN_CRIT "Unable to unreigster genl_family\n");
1348 }
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001349
1350#ifdef CONFIG_NF_CONNTRACK_EVENTS
1351 nf_conntrack_unregister_notifier(&init_net, &fast_classifier_conntrack_notifier);
1352
1353#endif
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001354 nf_unregister_hooks(fast_classifier_ipv4_ops_post_routing, ARRAY_SIZE(fast_classifier_ipv4_ops_post_routing));
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001355
1356 unregister_inetaddr_notifier(&sc->inet_notifier);
1357 unregister_netdevice_notifier(&sc->dev_notifier);
1358
1359 kobject_put(sc->sys_fast_classifier);
Matthew McClintock6f29aa12013-11-06 15:49:01 -06001360}
1361
Matthew McClintocke1bcfe42013-11-22 15:33:09 -06001362module_init(fast_classifier_init)
1363module_exit(fast_classifier_exit)
1364
1365MODULE_AUTHOR("Qualcomm Atheros Inc.");
1366MODULE_DESCRIPTION("Shortcut Forwarding Engine - Connection Manager");
Matthew McClintocka3221942014-01-16 11:44:26 -06001367MODULE_LICENSE("Dual BSD/GPL");
Matthew McClintock6f29aa12013-11-06 15:49:01 -06001368