1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter-feeds.git synced 2025-03-09 15:40:03 +00:00

Add Qualcomm Shortcut FE driver support

This commit is contained in:
Ycarus (Yannick Chabanois) 2021-04-14 21:02:45 +02:00
parent 3b1c0ae6c8
commit e3ea12a28f
22 changed files with 12374 additions and 0 deletions

View file

@ -0,0 +1,10 @@
obj-$(CONFIG_FAST_CLASSIFIER) += fast-classifier.o
ifeq ($(SFE_SUPPORT_IPV6),)
SFE_SUPPORT_IPV6=y
endif
ccflags-$(SFE_SUPPORT_IPV6) += -DSFE_SUPPORT_IPV6
ccflags-y += -I$(obj)/../shortcut-fe
obj ?= .

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,57 @@
/*
* User space header to send message to the fast classifier
*
* Copyright (c) 2013,2016 The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/if_ether.h>
#define FAST_CLASSIFIER_GENL_VERSION (1)
#define FAST_CLASSIFIER_GENL_NAME "FC"
#define FAST_CLASSIFIER_GENL_MCGRP "FC_MCGRP"
#define FAST_CLASSIFIER_GENL_HDRSIZE (0)
enum {
FAST_CLASSIFIER_A_UNSPEC,
FAST_CLASSIFIER_A_TUPLE,
__FAST_CLASSIFIER_A_MAX,
};
#define FAST_CLASSIFIER_A_MAX (__FAST_CLASSIFIER_A_MAX - 1)
enum {
FAST_CLASSIFIER_C_UNSPEC,
FAST_CLASSIFIER_C_OFFLOAD,
FAST_CLASSIFIER_C_OFFLOADED,
FAST_CLASSIFIER_C_DONE,
__FAST_CLASSIFIER_C_MAX,
};
#define FAST_CLASSIFIER_C_MAX (__FAST_CLASSIFIER_C_MAX - 1)
struct fast_classifier_tuple {
unsigned short ethertype;
unsigned char proto;
union {
struct in_addr in;
struct in6_addr in6;
} src_saddr;
union {
struct in_addr in;
struct in6_addr in6;
} dst_saddr;
unsigned short sport;
unsigned short dport;
unsigned char smac[ETH_ALEN];
unsigned char dmac[ETH_ALEN];
};

View file

@ -0,0 +1,281 @@
/*
* Copyright (c) 2016 The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <arpa/inet.h>
#define NL_CLASSIFIER_GENL_VERSION 1
#define NL_CLASSIFIER_GENL_FAMILY "FC"
#define NL_CLASSIFIER_GENL_GROUP "FC_MCGRP"
#define NL_CLASSIFIER_GENL_HDRSIZE 0
enum NL_CLASSIFIER_CMD {
NL_CLASSIFIER_CMD_UNSPEC,
NL_CLASSIFIER_CMD_ACCEL,
NL_CLASSIFIER_CMD_ACCEL_OK,
NL_CLASSIFIER_CMD_CONNECTION_CLOSED,
NL_CLASSIFIER_CMD_MAX,
};
enum NL_CLASSIFIER_ATTR {
NL_CLASSIFIER_ATTR_UNSPEC,
NL_CLASSIFIER_ATTR_TUPLE,
NL_CLASSIFIER_ATTR_MAX,
};
union nl_classifier_tuple_ip {
struct in_addr in;
struct in6_addr in6;
};
struct nl_classifier_tuple {
unsigned short af;
unsigned char proto;
union nl_classifier_tuple_ip src_ip;
union nl_classifier_tuple_ip dst_ip;
unsigned short sport;
unsigned short dport;
unsigned char smac[6];
unsigned char dmac[6];
};
struct nl_classifier_instance {
struct nl_sock *sock;
int family_id;
int group_id;
int stop;
};
struct nl_classifier_instance nl_cls_inst;
static struct nla_policy nl_classifier_genl_policy[(NL_CLASSIFIER_ATTR_MAX+1)] = {
[NL_CLASSIFIER_ATTR_TUPLE] = { .type = NLA_UNSPEC },
};
void nl_classifier_dump_nl_tuple(struct nl_classifier_tuple *tuple)
{
char ip_str[64];
printf("protocol = %s\n", (tuple->proto == IPPROTO_UDP) ? "udp" : ((tuple->proto == IPPROTO_TCP) ? "tcp" : "unknown"));
printf("source ip = %s\n", inet_ntop(tuple->af, &tuple->src_ip, ip_str, sizeof(ip_str)));
printf("destination ip = %s\n", inet_ntop(tuple->af, &tuple->dst_ip, ip_str, sizeof(ip_str)));
printf("source port = %d\n", ntohs(tuple->sport));
printf("destination port = %d\n", ntohs(tuple->dport));
}
int nl_classifier_msg_recv(struct nl_msg *msg, void *arg)
{
struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct genlmsghdr *gnlh = nlmsg_data(nlh);
struct nlattr *attrs[(NL_CLASSIFIER_ATTR_MAX+1)];
genlmsg_parse(nlh, NL_CLASSIFIER_GENL_HDRSIZE, attrs, NL_CLASSIFIER_ATTR_MAX, nl_classifier_genl_policy);
switch (gnlh->cmd) {
case NL_CLASSIFIER_CMD_ACCEL_OK:
printf("Acceleration successful:\n");
nl_classifier_dump_nl_tuple(nla_data(attrs[NL_CLASSIFIER_ATTR_TUPLE]));
return NL_OK;
case NL_CLASSIFIER_CMD_CONNECTION_CLOSED:
printf("Connection is closed:\n");
nl_classifier_dump_nl_tuple(nla_data(attrs[NL_CLASSIFIER_ATTR_TUPLE]));
return NL_OK;
default:
printf("nl classifier received unknow message %d\n", gnlh->cmd);
}
return NL_SKIP;
}
void nl_classifier_offload(struct nl_classifier_instance *inst,
unsigned char proto, unsigned long *src_saddr,
unsigned long *dst_saddr, unsigned short sport,
unsigned short dport, int af)
{
struct nl_msg *msg;
int ret;
struct nl_classifier_tuple classifier_msg;
memset(&classifier_msg, 0, sizeof(classifier_msg));
classifier_msg.af = af;
classifier_msg.proto = proto;
memcpy(&classifier_msg.src_ip, src_saddr, (af == AF_INET ? 4 : 16));
memcpy(&classifier_msg.dst_ip, dst_saddr, (af == AF_INET ? 4 : 16));
classifier_msg.sport = sport;
classifier_msg.dport = dport;
msg = nlmsg_alloc();
if (!msg) {
printf("Unable to allocate message\n");
return;
}
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, inst->family_id,
NL_CLASSIFIER_GENL_HDRSIZE, NLM_F_REQUEST,
NL_CLASSIFIER_CMD_ACCEL, NL_CLASSIFIER_GENL_VERSION);
nla_put(msg, NL_CLASSIFIER_ATTR_TUPLE, sizeof(classifier_msg), &classifier_msg);
ret = nl_send_auto(inst->sock, msg);
if (ret < 0) {
printf("send netlink message failed.\n");
nlmsg_free(msg);
return;
}
nlmsg_free(msg);
printf("nl classifier offload connection successful\n");
}
int nl_classifier_init(struct nl_classifier_instance *inst)
{
int ret;
inst->sock = nl_socket_alloc();
if (!inst->sock) {
printf("Unable to allocation socket.\n");
return -1;
}
genl_connect(inst->sock);
inst->family_id = genl_ctrl_resolve(inst->sock, NL_CLASSIFIER_GENL_FAMILY);
if (inst->family_id < 0) {
printf("Unable to resolve family %s\n", NL_CLASSIFIER_GENL_FAMILY);
goto init_failed;
}
inst->group_id = genl_ctrl_resolve_grp(inst->sock, NL_CLASSIFIER_GENL_FAMILY, NL_CLASSIFIER_GENL_GROUP);
if (inst->group_id < 0) {
printf("Unable to resolve mcast group %s\n", NL_CLASSIFIER_GENL_GROUP);
goto init_failed;
}
ret = nl_socket_add_membership(inst->sock, inst->group_id);
if (ret < 0) {
printf("Unable to add membership\n");
goto init_failed;
}
nl_socket_disable_seq_check(inst->sock);
nl_socket_modify_cb(inst->sock, NL_CB_VALID, NL_CB_CUSTOM, nl_classifier_msg_recv, NULL);
printf("nl classifier init successful\n");
return 0;
init_failed:
if (inst->sock) {
nl_close(inst->sock);
nl_socket_free(inst->sock);
inst->sock = NULL;
}
return -1;
}
void nl_classifier_exit(struct nl_classifier_instance *inst)
{
if (inst->sock) {
nl_close(inst->sock);
nl_socket_free(inst->sock);
inst->sock = NULL;
}
printf("nl classifier exit successful\n");
}
int nl_classifier_parse_arg(int argc, char *argv[], unsigned char *proto, unsigned long *src_saddr,
unsigned long *dst_saddr, unsigned short *sport, unsigned short *dport, int *af)
{
int ret;
unsigned short port;
if (argc < 7) {
printf("help: nl_classifier <v4|v6> <udp|tcp> <source ip> <destination ip> <source port> <destination port>\n");
return -1;
}
if (0 == strncmp(argv[1], "v4", 2)) {
*af = AF_INET;
} else if (0 == strncmp(argv[1], "v6", 2)) {
*af = AF_INET6;
} else {
printf("Address family is not supported");
return -1;
}
if (0 == strncmp(argv[2], "udp", 3)) {
*proto = IPPROTO_UDP;
} else if (0 == strncmp(argv[2], "tcp", 3)) {
*proto = IPPROTO_TCP;
} else {
printf("Protocol is not supported");
return -1;
}
ret = inet_pton(*af, argv[3], src_saddr);
if (ret <= 0) {
printf("source ip has wrong format\n");
return -1;
}
ret = inet_pton(*af, argv[4], dst_saddr);
if (ret <= 0) {
printf("destination ip has wrong format\n");
return -1;
}
port = strtol(argv[5], NULL, 0);
*sport = htons(port);
port = strtol(argv[6], NULL, 0);
*dport = htons(port);
printf("nl classifier parse arguments successful\n");
return 0;
}
int main(int argc, char *argv[])
{
struct nl_classifier_instance *inst = &nl_cls_inst;
unsigned char proto;
unsigned long src_addr[4];
unsigned long dst_addr[4];
unsigned short sport;
unsigned short dport;
int af;
int ret;
ret = nl_classifier_parse_arg(argc, argv, &proto, src_addr, dst_addr, &sport, &dport, &af);
if (ret < 0) {
printf("Failed to parse arguments\n");
return ret;
}
ret = nl_classifier_init(inst);
if (ret < 0) {
printf("Unable to init generic netlink\n");
return ret;
}
nl_classifier_offload(inst, proto, src_addr, dst_addr, sport, dport, af);
/* main loop to listen on message */
while (!inst->stop) {
nl_recvmsgs_default(inst->sock);
}
nl_classifier_exit(inst);
return 0;
}

114
fast-classifier/src/sfe.h Normal file
View file

@ -0,0 +1,114 @@
/*
* sfe.h
* Shortcut forwarding engine.
*
* Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* The following are debug macros used throughout the SFE.
*
* The DEBUG_LEVEL enables the followings based on its value,
* when dynamic debug option is disabled.
*
* 0 = OFF
* 1 = ASSERTS / ERRORS
* 2 = 1 + WARN
* 3 = 2 + INFO
* 4 = 3 + TRACE
*/
#define DEBUG_LEVEL 2
#if (DEBUG_LEVEL < 1)
#define DEBUG_ASSERT(s, ...)
#define DEBUG_ERROR(s, ...)
#else
#define DEBUG_ASSERT(c, s, ...) if (!(c)) { pr_emerg("ASSERT: %s:%d:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__); BUG(); }
#define DEBUG_ERROR(s, ...) pr_err("%s:%d:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
#if defined(CONFIG_DYNAMIC_DEBUG)
/*
* Compile messages for dynamic enable/disable
*/
#define DEBUG_WARN(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define DEBUG_INFO(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define DEBUG_TRACE(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
/*
* Statically compile messages at different levels
*/
#if (DEBUG_LEVEL < 2)
#define DEBUG_WARN(s, ...)
#else
#define DEBUG_WARN(s, ...) pr_warn("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
#if (DEBUG_LEVEL < 3)
#define DEBUG_INFO(s, ...)
#else
#define DEBUG_INFO(s, ...) pr_notice("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
#if (DEBUG_LEVEL < 4)
#define DEBUG_TRACE(s, ...)
#else
#define DEBUG_TRACE(s, ...) pr_info("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
#endif
#ifdef CONFIG_NF_FLOW_COOKIE
typedef int (*flow_cookie_set_func_t)(u32 protocol, __be32 src_ip, __be16 src_port,
__be32 dst_ip, __be16 dst_port, u16 flow_cookie);
/*
* sfe_register_flow_cookie_cb
* register a function in SFE to let SFE use this function to configure flow cookie for a flow
*
* Hardware driver which support flow cookie should register a callback function in SFE. Then SFE
* can use this function to configure flow cookie for a flow.
* return: 0, success; !=0, fail
*/
int sfe_register_flow_cookie_cb(flow_cookie_set_func_t cb);
/*
* sfe_unregister_flow_cookie_cb
* unregister function which is used to configure flow cookie for a flow
*
* return: 0, success; !=0, fail
*/
int sfe_unregister_flow_cookie_cb(flow_cookie_set_func_t cb);
typedef int (*sfe_ipv6_flow_cookie_set_func_t)(u32 protocol, __be32 src_ip[4], __be16 src_port,
__be32 dst_ip[4], __be16 dst_port, u16 flow_cookie);
/*
* sfe_ipv6_register_flow_cookie_cb
* register a function in SFE to let SFE use this function to configure flow cookie for a flow
*
* Hardware driver which support flow cookie should register a callback function in SFE. Then SFE
* can use this function to configure flow cookie for a flow.
* return: 0, success; !=0, fail
*/
int sfe_ipv6_register_flow_cookie_cb(sfe_ipv6_flow_cookie_set_func_t cb);
/*
* sfe_ipv6_unregister_flow_cookie_cb
* unregister function which is used to configure flow cookie for a flow
*
* return: 0, success; !=0, fail
*/
int sfe_ipv6_unregister_flow_cookie_cb(sfe_ipv6_flow_cookie_set_func_t cb);
#endif /*CONFIG_NF_FLOW_COOKIE*/

View file

@ -0,0 +1,195 @@
/*
* sfe_backport.h
* Shortcut forwarding engine compatible header file.
*
* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/version.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
#include <net/netfilter/nf_conntrack_timeout.h>
#else
enum udp_conntrack {
UDP_CT_UNREPLIED,
UDP_CT_REPLIED,
UDP_CT_MAX
};
static inline unsigned int *
nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct,
struct nf_conntrack_l4proto *l4proto)
{
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
struct nf_conn_timeout *timeout_ext;
unsigned int *timeouts;
timeout_ext = nf_ct_timeout_find(ct);
if (timeout_ext)
timeouts = NF_CT_TIMEOUT_EXT_DATA(timeout_ext);
else
timeouts = l4proto->get_timeouts(net);
return timeouts;
#else
return l4proto->get_timeouts(net);
#endif /*CONFIG_NF_CONNTRACK_TIMEOUT*/
}
#endif /*KERNEL_VERSION(3, 7, 0)*/
#endif /*KERNEL_VERSION(3, 4, 0)*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
#define sfe_define_post_routing_hook(FN_NAME, HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN) \
static unsigned int FN_NAME(void *priv, \
struct sk_buff *SKB, \
const struct nf_hook_state *state)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0))
#define sfe_define_post_routing_hook(FN_NAME, HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN) \
static unsigned int FN_NAME(const struct nf_hook_ops *OPS, \
struct sk_buff *SKB, \
const struct net_device *UNUSED, \
const struct net_device *OUT, \
int (*OKFN)(struct sk_buff *))
#else
#define sfe_define_post_routing_hook(FN_NAME, HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN) \
static unsigned int FN_NAME(unsigned int HOOKNUM, \
struct sk_buff *SKB, \
const struct net_device *UNUSED, \
const struct net_device *OUT, \
int (*OKFN)(struct sk_buff *))
#endif
#define sfe_cm_ipv4_post_routing_hook(HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN) \
sfe_define_post_routing_hook(__sfe_cm_ipv4_post_routing_hook, HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN)
#define sfe_cm_ipv6_post_routing_hook(HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN) \
sfe_define_post_routing_hook(__sfe_cm_ipv6_post_routing_hook, HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN)
#define fast_classifier_ipv4_post_routing_hook(HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN) \
sfe_define_post_routing_hook(__fast_classifier_ipv4_post_routing_hook, HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN)
#define fast_classifier_ipv6_post_routing_hook(HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN) \
sfe_define_post_routing_hook(__fast_classifier_ipv6_post_routing_hook, HOOKNUM, OPS, SKB, UNUSED, OUT, OKFN)
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
#define SFE_IPV4_NF_POST_ROUTING_HOOK(fn) \
{ \
.hook = fn, \
.pf = NFPROTO_IPV4, \
.hooknum = NF_INET_POST_ROUTING, \
.priority = NF_IP_PRI_NAT_SRC + 1, \
}
#else
#define SFE_IPV4_NF_POST_ROUTING_HOOK(fn) \
{ \
.hook = fn, \
.owner = THIS_MODULE, \
.pf = NFPROTO_IPV4, \
.hooknum = NF_INET_POST_ROUTING, \
.priority = NF_IP_PRI_NAT_SRC + 1, \
}
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
#define SFE_IPV6_NF_POST_ROUTING_HOOK(fn) \
{ \
.hook = fn, \
.pf = NFPROTO_IPV6, \
.hooknum = NF_INET_POST_ROUTING, \
.priority = NF_IP_PRI_NAT_SRC + 1, \
}
#else
#define SFE_IPV6_NF_POST_ROUTING_HOOK(fn) \
{ \
.hook = fn, \
.owner = THIS_MODULE, \
.pf = NFPROTO_IPV6, \
.hooknum = NF_INET_POST_ROUTING, \
.priority = NF_IP6_PRI_NAT_SRC + 1, \
}
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0))
#define SFE_NF_CT_DEFAULT_ZONE (&nf_ct_zone_dflt)
#else
#define SFE_NF_CT_DEFAULT_ZONE NF_CT_DEFAULT_ZONE
#endif
/*
* sfe_dev_get_master
* get master of bridge port, and hold it
*/
static inline struct net_device *sfe_dev_get_master(struct net_device *dev)
{
struct net_device *master;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
rcu_read_lock();
master = netdev_master_upper_dev_get_rcu(dev);
if (master)
dev_hold(master);
rcu_read_unlock();
#else
master = dev->master;
if (master)
dev_hold(master);
#endif
return master;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
#define SFE_DEV_EVENT_PTR(PTR) netdev_notifier_info_to_dev(PTR)
#else
#define SFE_DEV_EVENT_PTR(PTR) (struct net_device *)(PTR)
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0))
#define SFE_NF_CONN_ACCT(NM) struct nf_conn_acct *NM
#else
#define SFE_NF_CONN_ACCT(NM) struct nf_conn_counter *NM
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0))
#define SFE_ACCT_COUNTER(NM) ((NM)->counter)
#else
#define SFE_ACCT_COUNTER(NM) (NM)
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
#define sfe_hash_for_each_possible(name, obj, node, member, key) \
hash_for_each_possible(name, obj, member, key)
#else
#define sfe_hash_for_each_possible(name, obj, node, member, key) \
hash_for_each_possible(name, obj, node, member, key)
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
#define sfe_hash_for_each(name, bkt, node, obj, member) \
hash_for_each(name, bkt, obj, member)
#else
#define sfe_hash_for_each(name, bkt, node, obj, member) \
hash_for_each(name, bkt, node, obj, member)
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))
#define sfe_dst_get_neighbour(dst, daddr) dst_neigh_lookup(dst, daddr)
#else
static inline struct neighbour *
sfe_dst_get_neighbour(struct dst_entry *dst, void *daddr)
{
struct neighbour *neigh = dst_get_neighbour_noref(dst);
if (neigh)
neigh_hold(neigh);
return neigh;
}
#endif

View file

@ -0,0 +1,259 @@
/*
* sfe_cm.h
* Shortcut forwarding engine.
*
* Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* connection flags.
*/
#define SFE_CREATE_FLAG_NO_SEQ_CHECK BIT(0)
/* Indicates that we should not check sequence numbers */
#define SFE_CREATE_FLAG_REMARK_PRIORITY BIT(1)
/* Indicates that we should remark priority of skb */
#define SFE_CREATE_FLAG_REMARK_DSCP BIT(2)
/* Indicates that we should remark DSCP of packet */
/*
* IPv6 address structure
*/
struct sfe_ipv6_addr {
__be32 addr[4];
};
typedef union {
__be32 ip;
struct sfe_ipv6_addr ip6[1];
} sfe_ip_addr_t;
/*
* connection creation structure.
*/
struct sfe_connection_create {
int protocol;
struct net_device *src_dev;
struct net_device *dest_dev;
u32 flags;
u32 src_mtu;
u32 dest_mtu;
sfe_ip_addr_t src_ip;
sfe_ip_addr_t src_ip_xlate;
sfe_ip_addr_t dest_ip;
sfe_ip_addr_t dest_ip_xlate;
__be16 src_port;
__be16 src_port_xlate;
__be16 dest_port;
__be16 dest_port_xlate;
u8 src_mac[ETH_ALEN];
u8 src_mac_xlate[ETH_ALEN];
u8 dest_mac[ETH_ALEN];
u8 dest_mac_xlate[ETH_ALEN];
u8 src_td_window_scale;
u32 src_td_max_window;
u32 src_td_end;
u32 src_td_max_end;
u8 dest_td_window_scale;
u32 dest_td_max_window;
u32 dest_td_end;
u32 dest_td_max_end;
u32 mark;
#ifdef CONFIG_XFRM
u32 original_accel;
u32 reply_accel;
#endif
u32 src_priority;
u32 dest_priority;
u32 src_dscp;
u32 dest_dscp;
};
/*
* connection destruction structure.
*/
struct sfe_connection_destroy {
int protocol;
sfe_ip_addr_t src_ip;
sfe_ip_addr_t dest_ip;
__be16 src_port;
__be16 dest_port;
};
typedef enum sfe_sync_reason {
SFE_SYNC_REASON_STATS, /* Sync is to synchronize stats */
SFE_SYNC_REASON_FLUSH, /* Sync is to flush a entry */
SFE_SYNC_REASON_DESTROY /* Sync is to destroy a entry(requested by connection manager) */
} sfe_sync_reason_t;
/*
* Structure used to sync connection stats/state back within the system.
*
* NOTE: The addresses here are NON-NAT addresses, i.e. the true endpoint addressing.
* 'src' is the creator of the connection.
*/
struct sfe_connection_sync {
struct net_device *src_dev;
struct net_device *dest_dev;
int is_v6; /* Is it for ipv6? */
int protocol; /* IP protocol number (IPPROTO_...) */
sfe_ip_addr_t src_ip; /* Non-NAT source address, i.e. the creator of the connection */
sfe_ip_addr_t src_ip_xlate; /* NATed source address */
__be16 src_port; /* Non-NAT source port */
__be16 src_port_xlate; /* NATed source port */
sfe_ip_addr_t dest_ip; /* Non-NAT destination address, i.e. to whom the connection was created */
sfe_ip_addr_t dest_ip_xlate; /* NATed destination address */
__be16 dest_port; /* Non-NAT destination port */
__be16 dest_port_xlate; /* NATed destination port */
u32 src_td_max_window;
u32 src_td_end;
u32 src_td_max_end;
u64 src_packet_count;
u64 src_byte_count;
u32 src_new_packet_count;
u32 src_new_byte_count;
u32 dest_td_max_window;
u32 dest_td_end;
u32 dest_td_max_end;
u64 dest_packet_count;
u64 dest_byte_count;
u32 dest_new_packet_count;
u32 dest_new_byte_count;
u32 reason; /* reason for stats sync message, i.e. destroy, flush, period sync */
u64 delta_jiffies; /* Time to be added to the current timeout to keep the connection alive */
};
/*
* connection mark structure
*/
struct sfe_connection_mark {
int protocol;
sfe_ip_addr_t src_ip;
sfe_ip_addr_t dest_ip;
__be16 src_port;
__be16 dest_port;
u32 mark;
};
/*
* Expose the hook for the receive processing.
*/
extern int (*athrs_fast_nat_recv)(struct sk_buff *skb);
/*
* Expose what should be a static flag in the TCP connection tracker.
*/
extern int nf_ct_tcp_no_window_check;
/*
* This callback will be called in a timer
* at 100 times per second to sync stats back to
* Linux connection track.
*
* A RCU lock is taken to prevent this callback
* from unregistering.
*/
typedef void (*sfe_sync_rule_callback_t)(struct sfe_connection_sync *);
/*
* IPv4 APIs used by connection manager
*/
int sfe_ipv4_recv(struct net_device *dev, struct sk_buff *skb);
int sfe_ipv4_create_rule(struct sfe_connection_create *sic);
void sfe_ipv4_destroy_rule(struct sfe_connection_destroy *sid);
void sfe_ipv4_destroy_all_rules_for_dev(struct net_device *dev);
void sfe_ipv4_register_sync_rule_callback(sfe_sync_rule_callback_t callback);
void sfe_ipv4_update_rule(struct sfe_connection_create *sic);
void sfe_ipv4_mark_rule(struct sfe_connection_mark *mark);
#ifdef SFE_SUPPORT_IPV6
/*
* IPv6 APIs used by connection manager
*/
int sfe_ipv6_recv(struct net_device *dev, struct sk_buff *skb);
int sfe_ipv6_create_rule(struct sfe_connection_create *sic);
void sfe_ipv6_destroy_rule(struct sfe_connection_destroy *sid);
void sfe_ipv6_destroy_all_rules_for_dev(struct net_device *dev);
void sfe_ipv6_register_sync_rule_callback(sfe_sync_rule_callback_t callback);
void sfe_ipv6_update_rule(struct sfe_connection_create *sic);
void sfe_ipv6_mark_rule(struct sfe_connection_mark *mark);
#else
static inline int sfe_ipv6_recv(struct net_device *dev, struct sk_buff *skb)
{
return 0;
}
static inline int sfe_ipv6_create_rule(struct sfe_connection_create *sic)
{
return 0;
}
static inline void sfe_ipv6_destroy_rule(struct sfe_connection_destroy *sid)
{
return;
}
static inline void sfe_ipv6_destroy_all_rules_for_dev(struct net_device *dev)
{
return;
}
static inline void sfe_ipv6_register_sync_rule_callback(sfe_sync_rule_callback_t callback)
{
return;
}
static inline void sfe_ipv6_update_rule(struct sfe_connection_create *sic)
{
return;
}
static inline void sfe_ipv6_mark_rule(struct sfe_connection_mark *mark)
{
return;
}
#endif
/*
* sfe_ipv6_addr_equal()
* compare ipv6 address
*
* return: 1, equal; 0, no equal
*/
static inline int sfe_ipv6_addr_equal(struct sfe_ipv6_addr *a,
struct sfe_ipv6_addr *b)
{
return a->addr[0] == b->addr[0] &&
a->addr[1] == b->addr[1] &&
a->addr[2] == b->addr[2] &&
a->addr[3] == b->addr[3];
}
/*
* sfe_ipv4_addr_equal()
* compare ipv4 address
*
* return: 1, equal; 0, no equal
*/
#define sfe_ipv4_addr_equal(a, b) ((u32)(a) == (u32)(b))
/*
* sfe_addr_equal()
* compare ipv4 or ipv6 address
*
* return: 1, equal; 0, no equal
*/
static inline int sfe_addr_equal(sfe_ip_addr_t *a,
sfe_ip_addr_t *b, int is_v4)
{
return is_v4 ? sfe_ipv4_addr_equal(a->ip, b->ip) : sfe_ipv6_addr_equal(a->ip6, b->ip6);
}

View file

@ -0,0 +1,232 @@
/*
* Copyright (c) 2013,2016 The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <errno.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <fast-classifier.h>
static struct nl_sock *sock;
static struct nl_sock *sock_event;
static int family;
static int grp_id;
static struct nla_policy fast_classifier_genl_policy[FAST_CLASSIFIER_A_MAX + 1] = {
[FAST_CLASSIFIER_A_TUPLE] = { .type = NLA_UNSPEC },
};
void dump_fc_tuple(struct fast_classifier_tuple *fc_msg)
{
char src_str[INET_ADDRSTRLEN];
char dst_str[INET_ADDRSTRLEN];
printf("TUPLE: %d, %s, %s, %d, %d"
" SMAC=%02x:%02x:%02x:%02x:%02x:%02x",
" DMAC=%02x:%02x:%02x:%02x:%02x:%02x\n",
fc_msg->proto,
inet_ntop(AF_INET,
&fc_msg->src_saddr.in.s_addr,
src_str,
INET_ADDRSTRLEN),
inet_ntop(AF_INET,
&fc_msg->dst_saddr.in.s_addr,
dst_str,
INET_ADDRSTRLEN),
fc_msg->sport, fc_msg->dport,
fc_msg->smac[0], fc_msg->smac[1], fc_msg->smac[2],
fc_msg->smac[3], fc_msg->smac[4], fc_msg->smac[5],
fc_msg->dmac[0], fc_msg->dmac[1], fc_msg->dmac[2],
fc_msg->dmac[3], fc_msg->dmac[4], fc_msg->dmac[5]);
}
static int parse_cb(struct nl_msg *msg, void *arg)
{
struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct genlmsghdr *gnlh = nlmsg_data(nlh);
struct nlattr *attrs[FAST_CLASSIFIER_A_MAX];
genlmsg_parse(nlh, 0, attrs, FAST_CLASSIFIER_A_MAX, fast_classifier_genl_policy);
switch (gnlh->cmd) {
case FAST_CLASSIFIER_C_OFFLOADED:
printf("Got a offloaded message\n");
dump_fc_tuple(nla_data(attrs[FAST_CLASSIFIER_A_TUPLE]));
return NL_OK;
case FAST_CLASSIFIER_C_DONE:
printf("Got a done message\n");
dump_fc_tuple(nla_data(attrs[FAST_CLASSIFIER_A_TUPLE]));
return NL_OK;
}
return NL_SKIP;
}
int fast_classifier_init(void)
{
int err;
sock = nl_socket_alloc();
if (!sock) {
printf("Unable to allocation socket.\n");
return -1;
}
genl_connect(sock);
sock_event = nl_socket_alloc();
if (!sock_event) {
nl_close(sock);
nl_socket_free(sock);
printf("Unable to allocation socket.\n");
return -1;
}
genl_connect(sock_event);
family = genl_ctrl_resolve(sock, FAST_CLASSIFIER_GENL_NAME);
if (family < 0) {
nl_close(sock_event);
nl_close(sock);
nl_socket_free(sock);
nl_socket_free(sock_event);
printf("Unable to resolve family\n");
return -1;
}
grp_id = genl_ctrl_resolve_grp(sock, FAST_CLASSIFIER_GENL_NAME,
FAST_CLASSIFIER_GENL_MCGRP);
if (grp_id < 0) {
printf("Unable to resolve mcast group\n");
return -1;
}
err = nl_socket_add_membership(sock_event, grp_id);
if (err < 0) {
printf("Unable to add membership\n");
return -1;
}
nl_socket_disable_seq_check(sock_event);
nl_socket_modify_cb(sock_event, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, NULL);
return 0;
}
void fast_classifier_close(void)
{
nl_close(sock_event);
nl_close(sock);
nl_socket_free(sock_event);
nl_socket_free(sock);
}
void fast_classifier_ipv4_offload(unsigned char proto, unsigned long src_saddr,
unsigned long dst_saddr, unsigned short sport,
unsigned short dport)
{
struct nl_msg *msg;
int ret;
#ifdef DEBUG
char src_str[INET_ADDRSTRLEN];
char dst_str[INET_ADDRSTRLEN];
#endif
struct fast_classifier_tuple fc_msg;
#ifdef DEBUG
printf("DEBUG: would offload: %d, %s, %s, %d, %d\n", proto,
inet_ntop(AF_INET, &src_saddr, src_str, INET_ADDRSTRLEN),
inet_ntop(AF_INET, &dst_saddr, dst_str, INET_ADDRSTRLEN),
sport, dport);
#endif
fc_msg.proto = proto;
fc_msg.src_saddr.in.s_addr = src_saddr;
fc_msg.dst_saddr.in.s_addr = dst_saddr;
fc_msg.sport = sport;
fc_msg.dport = dport;
fc_msg.smac[0] = 'a';
fc_msg.smac[1] = 'b';
fc_msg.smac[2] = 'c';
fc_msg.smac[3] = 'd';
fc_msg.smac[4] = 'e';
fc_msg.smac[5] = 'f';
fc_msg.dmac[0] = 'f';
fc_msg.dmac[1] = 'e';
fc_msg.dmac[2] = 'd';
fc_msg.dmac[3] = 'c';
fc_msg.dmac[4] = 'b';
fc_msg.dmac[5] = 'a';
if (fast_classifier_init() < 0) {
printf("Unable to init generic netlink\n");
exit(1);
}
msg = nlmsg_alloc();
if (!msg) {
nl_socket_free(sock);
printf("Unable to allocate message\n");
return;
}
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family,
FAST_CLASSIFIER_GENL_HDRSIZE, NLM_F_REQUEST,
FAST_CLASSIFIER_C_OFFLOAD, FAST_CLASSIFIER_GENL_VERSION);
nla_put(msg, 1, sizeof(fc_msg), &fc_msg);
ret = nl_send_auto_complete(sock, msg);
nlmsg_free(msg);
if (ret < 0) {
printf("nlmsg_free failed");
nl_close(sock);
nl_socket_free(sock);
return;
}
ret = nl_wait_for_ack(sock);
if (ret < 0) {
printf("wait for ack failed");
nl_close(sock);
nl_socket_free(sock);
return;
}
}
void fast_classifier_listen_for_messages(void)
{
printf("waiting for netlink events\n");
while (1) {
nl_recvmsgs_default(sock_event);
}
}
int main(int argc, char *argv[])
{
if (fast_classifier_init() < 0) {
printf("Unable to init generic netlink\n");
exit(1);
}
fast_classifier_ipv4_offload('a', 0, 0, 0, 0);
/* this never returns */
fast_classifier_listen_for_messages();
fast_classifier_close();
return 0;
}